bittensor-cli 8.4.3__py3-none-any.whl → 9.0.0rc2__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 -2
- bittensor_cli/cli.py +1508 -1385
- bittensor_cli/src/__init__.py +627 -197
- bittensor_cli/src/bittensor/balances.py +41 -8
- bittensor_cli/src/bittensor/chain_data.py +557 -428
- bittensor_cli/src/bittensor/extrinsics/registration.py +161 -47
- bittensor_cli/src/bittensor/extrinsics/root.py +14 -8
- bittensor_cli/src/bittensor/extrinsics/transfer.py +14 -21
- bittensor_cli/src/bittensor/minigraph.py +46 -8
- bittensor_cli/src/bittensor/subtensor_interface.py +572 -253
- bittensor_cli/src/bittensor/utils.py +326 -75
- bittensor_cli/src/commands/stake/__init__.py +154 -0
- bittensor_cli/src/commands/stake/children_hotkeys.py +121 -87
- bittensor_cli/src/commands/stake/move.py +1000 -0
- bittensor_cli/src/commands/stake/stake.py +1637 -1264
- bittensor_cli/src/commands/subnets/__init__.py +0 -0
- bittensor_cli/src/commands/subnets/price.py +867 -0
- bittensor_cli/src/commands/subnets/subnets.py +2055 -0
- bittensor_cli/src/commands/sudo.py +529 -26
- bittensor_cli/src/commands/wallets.py +234 -544
- bittensor_cli/src/commands/weights.py +15 -11
- {bittensor_cli-8.4.3.dist-info → bittensor_cli-9.0.0rc2.dist-info}/METADATA +7 -4
- bittensor_cli-9.0.0rc2.dist-info/RECORD +32 -0
- bittensor_cli/src/bittensor/async_substrate_interface.py +0 -2748
- bittensor_cli/src/commands/root.py +0 -1752
- bittensor_cli/src/commands/subnets.py +0 -897
- bittensor_cli-8.4.3.dist-info/RECORD +0 -31
- {bittensor_cli-8.4.3.dist-info → bittensor_cli-9.0.0rc2.dist-info}/WHEEL +0 -0
- {bittensor_cli-8.4.3.dist-info → bittensor_cli-9.0.0rc2.dist-info}/entry_points.txt +0 -0
- {bittensor_cli-8.4.3.dist-info → bittensor_cli-9.0.0rc2.dist-info}/top_level.txt +0 -0
@@ -18,15 +18,19 @@ import typing
|
|
18
18
|
from typing import Optional
|
19
19
|
import subprocess
|
20
20
|
|
21
|
+
import backoff
|
21
22
|
from bittensor_wallet import Wallet
|
23
|
+
from bittensor_wallet.errors import KeyFileError
|
22
24
|
from Crypto.Hash import keccak
|
23
25
|
import numpy as np
|
24
26
|
from rich.prompt import Confirm
|
25
27
|
from rich.console import Console
|
26
28
|
from rich.status import Status
|
27
|
-
from
|
29
|
+
from async_substrate_interface.errors import SubstrateRequestException
|
28
30
|
|
31
|
+
from bittensor_cli.src import COLOR_PALETTE
|
29
32
|
from bittensor_cli.src.bittensor.chain_data import NeuronInfo
|
33
|
+
from bittensor_cli.src.bittensor.balances import Balance
|
30
34
|
from bittensor_cli.src.bittensor.utils import (
|
31
35
|
console,
|
32
36
|
err_console,
|
@@ -35,8 +39,6 @@ from bittensor_cli.src.bittensor.utils import (
|
|
35
39
|
get_human_readable,
|
36
40
|
print_verbose,
|
37
41
|
print_error,
|
38
|
-
unlock_key,
|
39
|
-
hex_to_bytes,
|
40
42
|
)
|
41
43
|
|
42
44
|
if typing.TYPE_CHECKING:
|
@@ -436,7 +438,7 @@ async def is_hotkey_registered(
|
|
436
438
|
subtensor: "SubtensorInterface", netuid: int, hotkey_ss58: str
|
437
439
|
) -> bool:
|
438
440
|
"""Checks to see if the hotkey is registered on a given netuid"""
|
439
|
-
_result = await subtensor.
|
441
|
+
_result = await subtensor.query(
|
440
442
|
module="SubtensorModule",
|
441
443
|
storage_function="Uids",
|
442
444
|
params=[netuid, hotkey_ss58],
|
@@ -487,22 +489,18 @@ async def register_extrinsic(
|
|
487
489
|
"""
|
488
490
|
|
489
491
|
async def get_neuron_for_pubkey_and_subnet():
|
490
|
-
uid = await subtensor.
|
492
|
+
uid = await subtensor.query(
|
491
493
|
"SubtensorModule", "Uids", [netuid, wallet.hotkey.ss58_address]
|
492
494
|
)
|
493
495
|
if uid is None:
|
494
496
|
return NeuronInfo.get_null_neuron()
|
495
497
|
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
498
|
+
result = await subtensor.neuron_for_uid(
|
499
|
+
uid=uid,
|
500
|
+
netuid=netuid,
|
501
|
+
block_hash=subtensor.substrate.last_block_hash,
|
500
502
|
)
|
501
|
-
|
502
|
-
if not (result := json_body.get("result", None)):
|
503
|
-
return NeuronInfo.get_null_neuron()
|
504
|
-
|
505
|
-
return NeuronInfo.from_vec_u8(bytes(result))
|
503
|
+
return result
|
506
504
|
|
507
505
|
print_verbose("Checking subnet status")
|
508
506
|
if not await subtensor.subnet_exists(netuid):
|
@@ -526,9 +524,9 @@ async def register_extrinsic(
|
|
526
524
|
if prompt:
|
527
525
|
if not Confirm.ask(
|
528
526
|
f"Continue Registration?\n"
|
529
|
-
f" hotkey ({wallet.hotkey_str}):\t[
|
530
|
-
f" coldkey ({wallet.name}):\t[
|
531
|
-
f" network:\t\t[
|
527
|
+
f" hotkey [{COLOR_PALETTE['GENERAL']['HOTKEY']}]({wallet.hotkey_str})[/{COLOR_PALETTE['GENERAL']['HOTKEY']}]:\t[{COLOR_PALETTE['GENERAL']['HOTKEY']}]{wallet.hotkey.ss58_address}[/{COLOR_PALETTE['GENERAL']['HOTKEY']}]\n"
|
528
|
+
f" coldkey [{COLOR_PALETTE['GENERAL']['COLDKEY']}]({wallet.name})[/{COLOR_PALETTE['GENERAL']['COLDKEY']}]:\t[{COLOR_PALETTE['GENERAL']['COLDKEY']}]{wallet.coldkeypub.ss58_address}[/{COLOR_PALETTE['GENERAL']['COLDKEY']}]\n"
|
529
|
+
f" network:\t\t[{COLOR_PALETTE['GENERAL']['LINKS']}]{subtensor.network}[/{COLOR_PALETTE['GENERAL']['LINKS']}]\n"
|
532
530
|
):
|
533
531
|
return False
|
534
532
|
|
@@ -581,7 +579,7 @@ async def register_extrinsic(
|
|
581
579
|
)
|
582
580
|
if is_registered:
|
583
581
|
err_console.print(
|
584
|
-
f":white_heavy_check_mark: [
|
582
|
+
f":white_heavy_check_mark: [dark_sea_green3]Already registered on netuid:{netuid}[/dark_sea_green3]"
|
585
583
|
)
|
586
584
|
return True
|
587
585
|
|
@@ -618,15 +616,18 @@ async def register_extrinsic(
|
|
618
616
|
if not success:
|
619
617
|
success, err_msg = (
|
620
618
|
False,
|
621
|
-
format_error_message(
|
619
|
+
format_error_message(
|
620
|
+
await response.error_message,
|
621
|
+
substrate=subtensor.substrate,
|
622
|
+
),
|
622
623
|
)
|
623
624
|
# Look error here
|
624
625
|
# https://github.com/opentensor/subtensor/blob/development/pallets/subtensor/src/errors.rs
|
625
626
|
|
626
627
|
if "HotKeyAlreadyRegisteredInSubNet" in err_msg:
|
627
628
|
console.print(
|
628
|
-
f":white_heavy_check_mark: [
|
629
|
-
f"[bold]subnet:{netuid}[/bold][/
|
629
|
+
f":white_heavy_check_mark: [dark_sea_green3]Already Registered on "
|
630
|
+
f"[bold]subnet:{netuid}[/bold][/dark_sea_green3]"
|
630
631
|
)
|
631
632
|
return True
|
632
633
|
err_console.print(
|
@@ -644,7 +645,7 @@ async def register_extrinsic(
|
|
644
645
|
)
|
645
646
|
if is_registered:
|
646
647
|
console.print(
|
647
|
-
":white_heavy_check_mark: [
|
648
|
+
":white_heavy_check_mark: [dark_sea_green3]Registered[/dark_sea_green3]"
|
648
649
|
)
|
649
650
|
return True
|
650
651
|
else:
|
@@ -671,6 +672,117 @@ async def register_extrinsic(
|
|
671
672
|
return False
|
672
673
|
|
673
674
|
|
675
|
+
async def burned_register_extrinsic(
|
676
|
+
subtensor: "SubtensorInterface",
|
677
|
+
wallet: Wallet,
|
678
|
+
netuid: int,
|
679
|
+
old_balance: Balance,
|
680
|
+
wait_for_inclusion: bool = True,
|
681
|
+
wait_for_finalization: bool = True,
|
682
|
+
prompt: bool = False,
|
683
|
+
) -> bool:
|
684
|
+
"""Registers the wallet to chain by recycling TAO.
|
685
|
+
|
686
|
+
:param subtensor: The SubtensorInterface object to use for the call, initialized
|
687
|
+
:param wallet: Bittensor wallet object.
|
688
|
+
:param netuid: The `netuid` of the subnet to register on.
|
689
|
+
:param old_balance: The wallet balance prior to the registration burn.
|
690
|
+
:param wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`, or returns
|
691
|
+
`False` if the extrinsic fails to enter the block within the timeout.
|
692
|
+
:param wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`,
|
693
|
+
or returns `False` if the extrinsic fails to be finalized within the timeout.
|
694
|
+
:param prompt: If `True`, the call waits for confirmation from the user before proceeding.
|
695
|
+
|
696
|
+
:return: Flag is `True` if extrinsic was finalized or included in the block. If we did not wait for
|
697
|
+
finalization/inclusion, the response is `True`.
|
698
|
+
"""
|
699
|
+
|
700
|
+
try:
|
701
|
+
wallet.unlock_coldkey()
|
702
|
+
except KeyFileError:
|
703
|
+
err_console.print("Error decrypting coldkey (possibly incorrect password)")
|
704
|
+
return False
|
705
|
+
|
706
|
+
with console.status(
|
707
|
+
f":satellite: Checking Account on [bold]subnet:{netuid}[/bold]...",
|
708
|
+
spinner="aesthetic",
|
709
|
+
) as status:
|
710
|
+
my_uid = await subtensor.query(
|
711
|
+
"SubtensorModule", "Uids", [netuid, wallet.hotkey.ss58_address]
|
712
|
+
)
|
713
|
+
|
714
|
+
print_verbose("Checking if already registered", status)
|
715
|
+
neuron = await subtensor.neuron_for_uid(
|
716
|
+
uid=my_uid,
|
717
|
+
netuid=netuid,
|
718
|
+
block_hash=subtensor.substrate.last_block_hash,
|
719
|
+
)
|
720
|
+
|
721
|
+
if not neuron.is_null:
|
722
|
+
console.print(
|
723
|
+
":white_heavy_check_mark: [dark_sea_green3]Already Registered[/dark_sea_green3]:\n"
|
724
|
+
f"uid: [{COLOR_PALETTE['GENERAL']['NETUID_EXTRA']}]{neuron.uid}[/{COLOR_PALETTE['GENERAL']['NETUID_EXTRA']}]\n"
|
725
|
+
f"netuid: [{COLOR_PALETTE['GENERAL']['NETUID']}]{neuron.netuid}[/{COLOR_PALETTE['GENERAL']['NETUID']}]\n"
|
726
|
+
f"hotkey: [{COLOR_PALETTE['GENERAL']['HOTKEY']}]{neuron.hotkey}[/{COLOR_PALETTE['GENERAL']['HOTKEY']}]\n"
|
727
|
+
f"coldkey: [{COLOR_PALETTE['GENERAL']['COLDKEY']}]{neuron.coldkey}[/{COLOR_PALETTE['GENERAL']['COLDKEY']}]"
|
728
|
+
)
|
729
|
+
return True
|
730
|
+
|
731
|
+
with console.status(
|
732
|
+
":satellite: Recycling TAO for Registration...", spinner="aesthetic"
|
733
|
+
):
|
734
|
+
call = await subtensor.substrate.compose_call(
|
735
|
+
call_module="SubtensorModule",
|
736
|
+
call_function="burned_register",
|
737
|
+
call_params={
|
738
|
+
"netuid": netuid,
|
739
|
+
"hotkey": wallet.hotkey.ss58_address,
|
740
|
+
},
|
741
|
+
)
|
742
|
+
success, err_msg = await subtensor.sign_and_send_extrinsic(
|
743
|
+
call, wallet, wait_for_inclusion, wait_for_finalization
|
744
|
+
)
|
745
|
+
|
746
|
+
if not success:
|
747
|
+
err_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}")
|
748
|
+
await asyncio.sleep(0.5)
|
749
|
+
return False
|
750
|
+
# Successful registration, final check for neuron and pubkey
|
751
|
+
else:
|
752
|
+
with console.status(":satellite: Checking Balance...", spinner="aesthetic"):
|
753
|
+
block_hash = await subtensor.substrate.get_chain_head()
|
754
|
+
new_balance, netuids_for_hotkey, my_uid = await asyncio.gather(
|
755
|
+
subtensor.get_balance(
|
756
|
+
wallet.coldkeypub.ss58_address,
|
757
|
+
block_hash=block_hash,
|
758
|
+
reuse_block=False,
|
759
|
+
),
|
760
|
+
subtensor.get_netuids_for_hotkey(
|
761
|
+
wallet.hotkey.ss58_address, block_hash=block_hash
|
762
|
+
),
|
763
|
+
subtensor.query(
|
764
|
+
"SubtensorModule", "Uids", [netuid, wallet.hotkey.ss58_address]
|
765
|
+
),
|
766
|
+
)
|
767
|
+
|
768
|
+
console.print(
|
769
|
+
"Balance:\n"
|
770
|
+
f" [blue]{old_balance}[/blue] :arrow_right: [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_balance}[/{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]"
|
771
|
+
)
|
772
|
+
|
773
|
+
if len(netuids_for_hotkey) > 0:
|
774
|
+
console.print(
|
775
|
+
f":white_heavy_check_mark: [green]Registered on netuid {netuid} with UID {my_uid}[/green]"
|
776
|
+
)
|
777
|
+
return True
|
778
|
+
else:
|
779
|
+
# neuron not found, try again
|
780
|
+
err_console.print(
|
781
|
+
":cross_mark: [red]Unknown error. Neuron not found.[/red]"
|
782
|
+
)
|
783
|
+
return False
|
784
|
+
|
785
|
+
|
674
786
|
async def run_faucet_extrinsic(
|
675
787
|
subtensor: "SubtensorInterface",
|
676
788
|
wallet: Wallet,
|
@@ -684,7 +796,7 @@ async def run_faucet_extrinsic(
|
|
684
796
|
tpb: int = 256,
|
685
797
|
num_processes: Optional[int] = None,
|
686
798
|
update_interval: Optional[int] = None,
|
687
|
-
log_verbose: bool =
|
799
|
+
log_verbose: bool = False,
|
688
800
|
max_successes: int = 3,
|
689
801
|
) -> tuple[bool, str]:
|
690
802
|
r"""Runs a continual POW to get a faucet of TAO on the test net.
|
@@ -723,8 +835,10 @@ async def run_faucet_extrinsic(
|
|
723
835
|
return False, "Requires torch"
|
724
836
|
|
725
837
|
# Unlock coldkey
|
726
|
-
|
727
|
-
|
838
|
+
try:
|
839
|
+
wallet.unlock_coldkey()
|
840
|
+
except KeyFileError:
|
841
|
+
return False, "There was an error unlocking your coldkey"
|
728
842
|
|
729
843
|
# Get previous balance.
|
730
844
|
old_balance = await subtensor.get_balance(wallet.coldkeypub.ss58_address)
|
@@ -732,12 +846,8 @@ async def run_faucet_extrinsic(
|
|
732
846
|
# Attempt rolling registration.
|
733
847
|
attempts = 1
|
734
848
|
successes = 1
|
735
|
-
pow_result: Optional[POWSolution]
|
736
849
|
while True:
|
737
850
|
try:
|
738
|
-
account_nonce = await subtensor.substrate.get_account_nonce(
|
739
|
-
wallet.coldkey.ss58_address
|
740
|
-
)
|
741
851
|
pow_result = None
|
742
852
|
while pow_result is None or await pow_result.is_stale(subtensor=subtensor):
|
743
853
|
# Solve latest POW.
|
@@ -746,7 +856,7 @@ async def run_faucet_extrinsic(
|
|
746
856
|
if prompt:
|
747
857
|
err_console.print("CUDA is not available.")
|
748
858
|
return False, "CUDA is not available."
|
749
|
-
pow_result = await create_pow(
|
859
|
+
pow_result: Optional[POWSolution] = await create_pow(
|
750
860
|
subtensor,
|
751
861
|
wallet,
|
752
862
|
-1,
|
@@ -759,7 +869,7 @@ async def run_faucet_extrinsic(
|
|
759
869
|
log_verbose=log_verbose,
|
760
870
|
)
|
761
871
|
else:
|
762
|
-
pow_result = await create_pow(
|
872
|
+
pow_result: Optional[POWSolution] = await create_pow(
|
763
873
|
subtensor,
|
764
874
|
wallet,
|
765
875
|
-1,
|
@@ -779,7 +889,7 @@ async def run_faucet_extrinsic(
|
|
779
889
|
},
|
780
890
|
)
|
781
891
|
extrinsic = await subtensor.substrate.create_signed_extrinsic(
|
782
|
-
call=call, keypair=wallet.coldkey
|
892
|
+
call=call, keypair=wallet.coldkey
|
783
893
|
)
|
784
894
|
response = await subtensor.substrate.submit_extrinsic(
|
785
895
|
extrinsic,
|
@@ -792,7 +902,7 @@ async def run_faucet_extrinsic(
|
|
792
902
|
if not await response.is_success:
|
793
903
|
err_console.print(
|
794
904
|
f":cross_mark: [red]Failed[/red]: "
|
795
|
-
f"{format_error_message(await response.error_message)}"
|
905
|
+
f"{format_error_message(await response.error_message, subtensor.substrate)}"
|
796
906
|
)
|
797
907
|
if attempts == max_allowed_attempts:
|
798
908
|
raise MaxAttemptsException
|
@@ -806,8 +916,8 @@ async def run_faucet_extrinsic(
|
|
806
916
|
wallet.coldkeypub.ss58_address
|
807
917
|
)
|
808
918
|
console.print(
|
809
|
-
f"Balance: [blue]{old_balance
|
810
|
-
f" [green]{new_balance
|
919
|
+
f"Balance: [blue]{old_balance}[/blue] :arrow_right:"
|
920
|
+
f" [green]{new_balance}[/green]"
|
811
921
|
)
|
812
922
|
old_balance = new_balance
|
813
923
|
|
@@ -864,7 +974,7 @@ async def _check_for_newest_block_and_update(
|
|
864
974
|
block_number, difficulty, block_hash = await _get_block_with_retry(
|
865
975
|
subtensor=subtensor, netuid=netuid
|
866
976
|
)
|
867
|
-
block_bytes =
|
977
|
+
block_bytes = bytes.fromhex(block_hash[2:])
|
868
978
|
|
869
979
|
update_curr_block(
|
870
980
|
curr_diff,
|
@@ -912,14 +1022,11 @@ async def _block_solver(
|
|
912
1022
|
limit = int(math.pow(2, 256)) - 1
|
913
1023
|
|
914
1024
|
# Establish communication queues
|
915
|
-
|
1025
|
+
# See the _Solver class for more information on the queues.
|
916
1026
|
stop_event = Event()
|
917
1027
|
stop_event.clear()
|
918
1028
|
|
919
1029
|
solution_queue = Queue()
|
920
|
-
if cuda:
|
921
|
-
num_processes = len(dev_id)
|
922
|
-
|
923
1030
|
finished_queues = [Queue() for _ in range(num_processes)]
|
924
1031
|
check_block = Lock()
|
925
1032
|
|
@@ -928,7 +1035,8 @@ async def _block_solver(
|
|
928
1035
|
)
|
929
1036
|
|
930
1037
|
if cuda:
|
931
|
-
|
1038
|
+
# Create a worker per CUDA device
|
1039
|
+
num_processes = len(dev_id)
|
932
1040
|
solvers = [
|
933
1041
|
_CUDASolver(
|
934
1042
|
i,
|
@@ -971,7 +1079,7 @@ async def _block_solver(
|
|
971
1079
|
subtensor=subtensor, netuid=netuid
|
972
1080
|
)
|
973
1081
|
|
974
|
-
block_bytes =
|
1082
|
+
block_bytes = bytes.fromhex(block_hash[2:])
|
975
1083
|
old_block_number = block_number
|
976
1084
|
# Set to current block
|
977
1085
|
_update_curr_block(
|
@@ -1246,9 +1354,11 @@ def _terminate_workers_and_wait_for_exit(
|
|
1246
1354
|
worker.terminate()
|
1247
1355
|
|
1248
1356
|
|
1357
|
+
# TODO verify this works with async
|
1358
|
+
@backoff.on_exception(backoff.constant, Exception, interval=1, max_tries=3)
|
1249
1359
|
async def _get_block_with_retry(
|
1250
1360
|
subtensor: "SubtensorInterface", netuid: int
|
1251
|
-
) -> tuple[int, int,
|
1361
|
+
) -> tuple[int, int, bytes]:
|
1252
1362
|
"""
|
1253
1363
|
Gets the current block number, difficulty, and block hash from the substrate node.
|
1254
1364
|
|
@@ -1260,9 +1370,10 @@ async def _get_block_with_retry(
|
|
1260
1370
|
:raises Exception: If the block hash is None.
|
1261
1371
|
:raises ValueError: If the difficulty is None.
|
1262
1372
|
"""
|
1263
|
-
|
1264
|
-
block_hash =
|
1265
|
-
|
1373
|
+
block_number = await subtensor.substrate.get_block_number(None)
|
1374
|
+
block_hash = await subtensor.substrate.get_block_hash(
|
1375
|
+
block_number
|
1376
|
+
) # TODO check if I need to do all this
|
1266
1377
|
try:
|
1267
1378
|
difficulty = (
|
1268
1379
|
1_000_000
|
@@ -1635,7 +1746,10 @@ async def swap_hotkey_extrinsic(
|
|
1635
1746
|
)
|
1636
1747
|
return False
|
1637
1748
|
|
1638
|
-
|
1749
|
+
try:
|
1750
|
+
wallet.unlock_coldkey()
|
1751
|
+
except KeyFileError:
|
1752
|
+
err_console.print("Error decrypting coldkey (possibly incorrect password)")
|
1639
1753
|
return False
|
1640
1754
|
|
1641
1755
|
if prompt:
|
@@ -21,12 +21,13 @@ import time
|
|
21
21
|
from typing import Union, List, TYPE_CHECKING
|
22
22
|
|
23
23
|
from bittensor_wallet import Wallet, Keypair
|
24
|
+
from bittensor_wallet.errors import KeyFileError
|
24
25
|
import numpy as np
|
25
26
|
from numpy.typing import NDArray
|
26
27
|
from rich.prompt import Confirm
|
27
28
|
from rich.table import Table, Column
|
28
29
|
from scalecodec import ScaleBytes, U16, Vec
|
29
|
-
from
|
30
|
+
from async_substrate_interface.errors import SubstrateRequestException
|
30
31
|
|
31
32
|
from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface
|
32
33
|
from bittensor_cli.src.bittensor.extrinsics.registration import is_hotkey_registered
|
@@ -36,7 +37,6 @@ from bittensor_cli.src.bittensor.utils import (
|
|
36
37
|
u16_normalized_float,
|
37
38
|
print_verbose,
|
38
39
|
format_error_message,
|
39
|
-
unlock_key,
|
40
40
|
)
|
41
41
|
|
42
42
|
if TYPE_CHECKING:
|
@@ -306,7 +306,10 @@ async def root_register_extrinsic(
|
|
306
306
|
the response is `True`.
|
307
307
|
"""
|
308
308
|
|
309
|
-
|
309
|
+
try:
|
310
|
+
wallet.unlock_coldkey()
|
311
|
+
except KeyFileError:
|
312
|
+
err_console.print("Error decrypting coldkey (possibly incorrect password)")
|
310
313
|
return False
|
311
314
|
|
312
315
|
print_verbose(f"Checking if hotkey ({wallet.hotkey_str}) is registered on root")
|
@@ -339,7 +342,7 @@ async def root_register_extrinsic(
|
|
339
342
|
|
340
343
|
# Successful registration, final check for neuron and pubkey
|
341
344
|
else:
|
342
|
-
uid = await subtensor.
|
345
|
+
uid = await subtensor.query(
|
343
346
|
module="SubtensorModule",
|
344
347
|
storage_function="Uids",
|
345
348
|
params=[0, wallet.hotkey.ss58_address],
|
@@ -416,7 +419,7 @@ async def set_root_weights_extrinsic(
|
|
416
419
|
else:
|
417
420
|
return False, await response.error_message
|
418
421
|
|
419
|
-
my_uid = await subtensor.
|
422
|
+
my_uid = await subtensor.query(
|
420
423
|
"SubtensorModule", "Uids", [0, wallet.hotkey.ss58_address]
|
421
424
|
)
|
422
425
|
|
@@ -424,7 +427,10 @@ async def set_root_weights_extrinsic(
|
|
424
427
|
err_console.print("Your hotkey is not registered to the root network")
|
425
428
|
return False
|
426
429
|
|
427
|
-
|
430
|
+
try:
|
431
|
+
wallet.unlock_coldkey()
|
432
|
+
except KeyFileError:
|
433
|
+
err_console.print("Error decrypting coldkey (possibly incorrect password)")
|
428
434
|
return False
|
429
435
|
|
430
436
|
# First convert types.
|
@@ -486,11 +492,11 @@ async def set_root_weights_extrinsic(
|
|
486
492
|
console.print(":white_heavy_check_mark: [green]Finalized[/green]")
|
487
493
|
return True
|
488
494
|
else:
|
489
|
-
fmt_err = format_error_message(error_message)
|
495
|
+
fmt_err = format_error_message(error_message, subtensor.substrate)
|
490
496
|
err_console.print(f":cross_mark: [red]Failed[/red]: {fmt_err}")
|
491
497
|
return False
|
492
498
|
|
493
499
|
except SubstrateRequestException as e:
|
494
|
-
fmt_err = format_error_message(e)
|
500
|
+
fmt_err = format_error_message(e, subtensor.substrate)
|
495
501
|
err_console.print(":cross_mark: [red]Failed[/red]: error:{}".format(fmt_err))
|
496
502
|
return False
|
@@ -1,8 +1,9 @@
|
|
1
1
|
import asyncio
|
2
2
|
|
3
3
|
from bittensor_wallet import Wallet
|
4
|
+
from bittensor_wallet.errors import KeyFileError
|
4
5
|
from rich.prompt import Confirm
|
5
|
-
from
|
6
|
+
from async_substrate_interface.errors import SubstrateRequestException
|
6
7
|
|
7
8
|
from bittensor_cli.src import NETWORK_EXPLORER_MAP
|
8
9
|
from bittensor_cli.src.bittensor.balances import Balance
|
@@ -14,8 +15,6 @@ from bittensor_cli.src.bittensor.utils import (
|
|
14
15
|
format_error_message,
|
15
16
|
get_explorer_url_for_network,
|
16
17
|
is_valid_bittensor_address_or_public_key,
|
17
|
-
print_error,
|
18
|
-
unlock_key,
|
19
18
|
)
|
20
19
|
|
21
20
|
|
@@ -24,7 +23,6 @@ async def transfer_extrinsic(
|
|
24
23
|
wallet: Wallet,
|
25
24
|
destination: str,
|
26
25
|
amount: Balance,
|
27
|
-
transfer_all: bool = False,
|
28
26
|
wait_for_inclusion: bool = True,
|
29
27
|
wait_for_finalization: bool = False,
|
30
28
|
keep_alive: bool = True,
|
@@ -36,7 +34,6 @@ async def transfer_extrinsic(
|
|
36
34
|
:param wallet: Bittensor wallet object to make transfer from.
|
37
35
|
:param destination: Destination public key address (ss58_address or ed25519) of recipient.
|
38
36
|
:param amount: Amount to stake as Bittensor balance.
|
39
|
-
:param transfer_all: Whether to transfer all funds from this wallet to the destination address.
|
40
37
|
:param wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`,
|
41
38
|
or returns `False` if the extrinsic fails to enter the block within the timeout.
|
42
39
|
:param wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning
|
@@ -64,14 +61,14 @@ async def transfer_extrinsic(
|
|
64
61
|
call=call, keypair=wallet.coldkeypub
|
65
62
|
)
|
66
63
|
except SubstrateRequestException as e:
|
67
|
-
payment_info = {"
|
64
|
+
payment_info = {"partial_fee": int(2e7)} # assume 0.02 Tao
|
68
65
|
err_console.print(
|
69
|
-
f":cross_mark: [red]Failed to get payment info[/red]
|
70
|
-
f" {format_error_message(e)}[/bold white]\n"
|
71
|
-
f" Defaulting to default transfer fee: {payment_info['
|
66
|
+
f":cross_mark: [red]Failed to get payment info[/red]:\n"
|
67
|
+
f" [bold white]{format_error_message(e)}[/bold white]\n"
|
68
|
+
f" Defaulting to default transfer fee: {payment_info['partial_fee']}"
|
72
69
|
)
|
73
70
|
|
74
|
-
return Balance.from_rao(payment_info["
|
71
|
+
return Balance.from_rao(payment_info["partial_fee"])
|
75
72
|
|
76
73
|
async def do_transfer() -> tuple[bool, str, str]:
|
77
74
|
"""
|
@@ -104,7 +101,7 @@ async def transfer_extrinsic(
|
|
104
101
|
return (
|
105
102
|
False,
|
106
103
|
"",
|
107
|
-
format_error_message(await response.error_message),
|
104
|
+
format_error_message(await response.error_message, subtensor.substrate),
|
108
105
|
)
|
109
106
|
|
110
107
|
# Validate destination address.
|
@@ -115,7 +112,10 @@ async def transfer_extrinsic(
|
|
115
112
|
return False
|
116
113
|
console.print(f"[dark_orange]Initiating transfer on network: {subtensor.network}")
|
117
114
|
# Unlock wallet coldkey.
|
118
|
-
|
115
|
+
try:
|
116
|
+
wallet.unlock_coldkey()
|
117
|
+
except KeyFileError:
|
118
|
+
err_console.print("Error decrypting coldkey (possibly incorrect password)")
|
119
119
|
return False
|
120
120
|
|
121
121
|
# Check balance.
|
@@ -126,13 +126,12 @@ async def transfer_extrinsic(
|
|
126
126
|
# check existential deposit and fee
|
127
127
|
print_verbose("Fetching existential and fee", status)
|
128
128
|
block_hash = await subtensor.substrate.get_chain_head()
|
129
|
-
|
129
|
+
account_balance, existential_deposit = await asyncio.gather(
|
130
130
|
subtensor.get_balance(
|
131
131
|
wallet.coldkeypub.ss58_address, block_hash=block_hash
|
132
132
|
),
|
133
133
|
subtensor.get_existential_deposit(block_hash=block_hash),
|
134
134
|
)
|
135
|
-
account_balance = account_balance_[wallet.coldkeypub.ss58_address]
|
136
135
|
fee = await get_transfer_fee()
|
137
136
|
|
138
137
|
if not keep_alive:
|
@@ -140,12 +139,6 @@ async def transfer_extrinsic(
|
|
140
139
|
existential_deposit = Balance(0)
|
141
140
|
|
142
141
|
# Check if we have enough balance.
|
143
|
-
if transfer_all is True:
|
144
|
-
amount = account_balance - fee - existential_deposit
|
145
|
-
if amount < Balance(0):
|
146
|
-
print_error("Not enough balance to transfer")
|
147
|
-
return False
|
148
|
-
|
149
142
|
if account_balance < (amount + fee + existential_deposit):
|
150
143
|
err_console.print(
|
151
144
|
":cross_mark: [bold red]Not enough balance[/bold red]:\n\n"
|
@@ -194,7 +187,7 @@ async def transfer_extrinsic(
|
|
194
187
|
)
|
195
188
|
console.print(
|
196
189
|
f"Balance:\n"
|
197
|
-
f" [blue]{account_balance}[/blue] :arrow_right: [green]{new_balance
|
190
|
+
f" [blue]{account_balance}[/blue] :arrow_right: [green]{new_balance}[/green]"
|
198
191
|
)
|
199
192
|
return True
|
200
193
|
|
@@ -3,7 +3,7 @@ import asyncio
|
|
3
3
|
import numpy as np
|
4
4
|
from numpy.typing import NDArray
|
5
5
|
|
6
|
-
from bittensor_cli.src.bittensor.chain_data import NeuronInfo
|
6
|
+
from bittensor_cli.src.bittensor.chain_data import NeuronInfo, SubnetState
|
7
7
|
from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface
|
8
8
|
from bittensor_cli.src.bittensor.utils import (
|
9
9
|
convert_root_weight_uids_and_vals_to_tensor,
|
@@ -18,6 +18,7 @@ class MiniGraph:
|
|
18
18
|
netuid: int,
|
19
19
|
neurons: list[NeuronInfo],
|
20
20
|
subtensor: "SubtensorInterface",
|
21
|
+
subnet_state: "SubnetState",
|
21
22
|
block: int,
|
22
23
|
):
|
23
24
|
self.neurons = neurons
|
@@ -62,12 +63,14 @@ class MiniGraph:
|
|
62
63
|
self.validator_trust = self._create_tensor(
|
63
64
|
[neuron.validator_trust for neuron in self.neurons], dtype=np.float32
|
64
65
|
)
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
[neuron.stake for neuron in self.neurons], dtype=np.float32
|
66
|
+
|
67
|
+
# Fetch stakes from subnet_state until we get updated data in NeuronInfo
|
68
|
+
global_stake_list, local_stake_list, stake_weights_list = self._process_stakes(
|
69
|
+
neurons, subnet_state
|
70
70
|
)
|
71
|
+
self.global_stake = self._create_tensor(global_stake_list, dtype=np.float32)
|
72
|
+
self.local_stake = self._create_tensor(local_stake_list, dtype=np.float32)
|
73
|
+
self.stake_weights = self._create_tensor(stake_weights_list, dtype=np.float32)
|
71
74
|
|
72
75
|
async def __aenter__(self):
|
73
76
|
if not self.weights:
|
@@ -120,6 +123,41 @@ class MiniGraph:
|
|
120
123
|
[neuron.bonds for neuron in self.neurons], "bonds"
|
121
124
|
)
|
122
125
|
|
126
|
+
def _process_stakes(
|
127
|
+
self,
|
128
|
+
neurons: list[NeuronInfo],
|
129
|
+
subnet_state: SubnetState,
|
130
|
+
) -> tuple[list[float], list[float], list[float]]:
|
131
|
+
"""
|
132
|
+
Processes the global_stake, local_stake, and stake_weights based on the neuron's hotkey.
|
133
|
+
|
134
|
+
Args:
|
135
|
+
neurons (List[NeuronInfo]): List of neurons.
|
136
|
+
subnet_state (SubnetState): The subnet state containing stake information.
|
137
|
+
|
138
|
+
Returns:
|
139
|
+
tuple[list[float], list[float], list[float]]: Lists of global_stake, local_stake, and stake_weights.
|
140
|
+
"""
|
141
|
+
global_stake_list = []
|
142
|
+
local_stake_list = []
|
143
|
+
stake_weights_list = []
|
144
|
+
hotkey_to_index = {
|
145
|
+
hotkey: idx for idx, hotkey in enumerate(subnet_state.hotkeys)
|
146
|
+
}
|
147
|
+
|
148
|
+
for neuron in neurons:
|
149
|
+
idx = hotkey_to_index.get(neuron.hotkey)
|
150
|
+
if idx is not None:
|
151
|
+
global_stake_list.append(subnet_state.global_stake[idx].tao)
|
152
|
+
local_stake_list.append(subnet_state.local_stake[idx].tao)
|
153
|
+
stake_weights_list.append(subnet_state.stake_weight[idx])
|
154
|
+
else:
|
155
|
+
global_stake_list.append(0.0)
|
156
|
+
local_stake_list.append(0.0)
|
157
|
+
stake_weights_list.append(0.0)
|
158
|
+
|
159
|
+
return global_stake_list, local_stake_list, stake_weights_list
|
160
|
+
|
123
161
|
def _process_weights_or_bonds(self, data, attribute: str) -> NDArray:
|
124
162
|
"""
|
125
163
|
Processes the raw weights or bonds data and converts it into a structured tensor format. This method handles
|
@@ -177,7 +215,7 @@ class MiniGraph:
|
|
177
215
|
"""
|
178
216
|
|
179
217
|
async def get_total_subnets():
|
180
|
-
_result = await self.subtensor.
|
218
|
+
_result = await self.subtensor.query(
|
181
219
|
module="SubtensorModule",
|
182
220
|
storage_function="TotalNetworks",
|
183
221
|
params=[],
|
@@ -186,7 +224,7 @@ class MiniGraph:
|
|
186
224
|
return _result
|
187
225
|
|
188
226
|
async def get_subnets():
|
189
|
-
_result = await self.subtensor.
|
227
|
+
_result = await self.subtensor.query(
|
190
228
|
module="SubtensorModule",
|
191
229
|
storage_function="TotalNetworks",
|
192
230
|
)
|