bittensor-cli 8.4.2__py3-none-any.whl → 9.0.0rc1__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 +1503 -1372
- bittensor_cli/src/__init__.py +625 -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 +123 -91
- 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 +2043 -0
- bittensor_cli/src/commands/sudo.py +529 -26
- bittensor_cli/src/commands/wallets.py +231 -535
- bittensor_cli/src/commands/weights.py +15 -11
- {bittensor_cli-8.4.2.dist-info → bittensor_cli-9.0.0rc1.dist-info}/METADATA +7 -4
- bittensor_cli-9.0.0rc1.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.2.dist-info/RECORD +0 -31
- {bittensor_cli-8.4.2.dist-info → bittensor_cli-9.0.0rc1.dist-info}/WHEEL +0 -0
- {bittensor_cli-8.4.2.dist-info → bittensor_cli-9.0.0rc1.dist-info}/entry_points.txt +0 -0
- {bittensor_cli-8.4.2.dist-info → bittensor_cli-9.0.0rc1.dist-info}/top_level.txt +0 -0
@@ -1,39 +1,31 @@
|
|
1
1
|
import asyncio
|
2
|
-
import binascii
|
3
2
|
import itertools
|
4
3
|
import os
|
5
|
-
import sys
|
6
4
|
from collections import defaultdict
|
7
|
-
from
|
8
|
-
from sys import getsizeof
|
9
|
-
from typing import Collection, Generator, Optional
|
5
|
+
from typing import Generator, Optional
|
10
6
|
|
11
7
|
import aiohttp
|
12
|
-
from bittensor_wallet import Wallet
|
8
|
+
from bittensor_wallet import Wallet, Keypair
|
13
9
|
from bittensor_wallet.errors import KeyFileError
|
14
10
|
from bittensor_wallet.keyfile import Keyfile
|
15
11
|
from fuzzywuzzy import fuzz
|
16
12
|
from rich import box
|
17
13
|
from rich.align import Align
|
18
|
-
from rich.prompt import Confirm
|
19
14
|
from rich.table import Column, Table
|
20
15
|
from rich.tree import Tree
|
21
16
|
from rich.padding import Padding
|
22
|
-
from rich.prompt import IntPrompt
|
23
17
|
import typer
|
24
18
|
|
19
|
+
from bittensor_cli.src import COLOR_PALETTE
|
25
20
|
from bittensor_cli.src.bittensor import utils
|
26
21
|
from bittensor_cli.src.bittensor.balances import Balance
|
27
22
|
from bittensor_cli.src.bittensor.chain_data import (
|
28
23
|
DelegateInfo,
|
29
24
|
NeuronInfoLite,
|
30
|
-
StakeInfo,
|
31
|
-
decode_account_id,
|
32
25
|
)
|
33
26
|
from bittensor_cli.src.bittensor.extrinsics.registration import (
|
34
27
|
run_faucet_extrinsic,
|
35
28
|
swap_hotkey_extrinsic,
|
36
|
-
is_hotkey_registered,
|
37
29
|
)
|
38
30
|
from bittensor_cli.src.bittensor.extrinsics.transfer import transfer_extrinsic
|
39
31
|
from bittensor_cli.src.bittensor.networking import int_to_ip
|
@@ -49,9 +41,8 @@ from bittensor_cli.src.bittensor.utils import (
|
|
49
41
|
get_hotkey_wallets_for_wallet,
|
50
42
|
is_valid_ss58_address,
|
51
43
|
validate_coldkey_presence,
|
52
|
-
|
53
|
-
|
54
|
-
hex_to_bytes,
|
44
|
+
get_subnet_name,
|
45
|
+
millify_tao,
|
55
46
|
)
|
56
47
|
|
57
48
|
|
@@ -78,21 +69,13 @@ async def regen_coldkey(
|
|
78
69
|
with open(json_path, "r") as f:
|
79
70
|
json_str = f.read()
|
80
71
|
try:
|
81
|
-
|
72
|
+
wallet.regenerate_coldkey(
|
82
73
|
mnemonic=mnemonic,
|
83
74
|
seed=seed,
|
84
75
|
json=(json_str, json_password) if all([json_str, json_password]) else None,
|
85
76
|
use_password=use_password,
|
86
77
|
overwrite=False,
|
87
78
|
)
|
88
|
-
|
89
|
-
if isinstance(new_wallet, Wallet):
|
90
|
-
console.print(
|
91
|
-
"\n✅ [dark_sea_green]Regenerated coldkey successfully!\n",
|
92
|
-
f"[dark_sea_green]Wallet name: ({new_wallet.name}), path: ({new_wallet.path}), coldkey ss58: ({new_wallet.coldkeypub.ss58_address})",
|
93
|
-
)
|
94
|
-
except ValueError:
|
95
|
-
print_error("Mnemonic phrase is invalid")
|
96
79
|
except KeyFileError:
|
97
80
|
print_error("KeyFileError: File is not writable")
|
98
81
|
|
@@ -104,16 +87,11 @@ async def regen_coldkey_pub(
|
|
104
87
|
):
|
105
88
|
"""Creates a new coldkeypub under this wallet."""
|
106
89
|
try:
|
107
|
-
|
90
|
+
wallet.regenerate_coldkeypub(
|
108
91
|
ss58_address=ss58_address,
|
109
92
|
public_key=public_key_hex,
|
110
93
|
overwrite=False,
|
111
94
|
)
|
112
|
-
if isinstance(new_coldkeypub, Wallet):
|
113
|
-
console.print(
|
114
|
-
"\n✅ [dark_sea_green]Regenerated coldkeypub successfully!\n",
|
115
|
-
f"[dark_sea_green]Wallet name: ({new_coldkeypub.name}), path: ({new_coldkeypub.path}), coldkey ss58: ({new_coldkeypub.coldkeypub.ss58_address})",
|
116
|
-
)
|
117
95
|
except KeyFileError:
|
118
96
|
print_error("KeyFileError: File is not writable")
|
119
97
|
|
@@ -136,20 +114,13 @@ async def regen_hotkey(
|
|
136
114
|
json_str = f.read()
|
137
115
|
|
138
116
|
try:
|
139
|
-
|
117
|
+
wallet.regenerate_hotkey(
|
140
118
|
mnemonic=mnemonic,
|
141
119
|
seed=seed,
|
142
120
|
json=(json_str, json_password) if all([json_str, json_password]) else None,
|
143
121
|
use_password=use_password,
|
144
122
|
overwrite=False,
|
145
123
|
)
|
146
|
-
if isinstance(new_hotkey, Wallet):
|
147
|
-
console.print(
|
148
|
-
"\n✅ [dark_sea_green]Regenerated hotkey successfully!\n",
|
149
|
-
f"[dark_sea_green]Wallet name: ({new_hotkey.name}), path: ({new_hotkey.path}), hotkey ss58: ({new_hotkey.hotkey.ss58_address})",
|
150
|
-
)
|
151
|
-
except ValueError:
|
152
|
-
print_error("Mnemonic phrase is invalid")
|
153
124
|
except KeyFileError:
|
154
125
|
print_error("KeyFileError: File is not writable")
|
155
126
|
|
@@ -158,14 +129,26 @@ async def new_hotkey(
|
|
158
129
|
wallet: Wallet,
|
159
130
|
n_words: int,
|
160
131
|
use_password: bool,
|
132
|
+
uri: Optional[str] = None,
|
161
133
|
):
|
162
134
|
"""Creates a new hotkey under this wallet."""
|
163
135
|
try:
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
136
|
+
if uri:
|
137
|
+
try:
|
138
|
+
keypair = Keypair.create_from_uri(uri)
|
139
|
+
except Exception as e:
|
140
|
+
print_error(f"Failed to create keypair from URI {uri}: {str(e)}")
|
141
|
+
wallet.set_hotkey(keypair=keypair, encrypt=use_password)
|
142
|
+
console.print(
|
143
|
+
f"[dark_sea_green]Hotkey created from URI: {uri}[/dark_sea_green]"
|
144
|
+
)
|
145
|
+
else:
|
146
|
+
wallet.create_new_hotkey(
|
147
|
+
n_words=n_words,
|
148
|
+
use_password=use_password,
|
149
|
+
overwrite=False,
|
150
|
+
)
|
151
|
+
console.print("[dark_sea_green]Hotkey created[/dark_sea_green]")
|
169
152
|
except KeyFileError:
|
170
153
|
print_error("KeyFileError: File is not writable")
|
171
154
|
|
@@ -174,14 +157,27 @@ async def new_coldkey(
|
|
174
157
|
wallet: Wallet,
|
175
158
|
n_words: int,
|
176
159
|
use_password: bool,
|
160
|
+
uri: Optional[str] = None,
|
177
161
|
):
|
178
162
|
"""Creates a new coldkey under this wallet."""
|
179
163
|
try:
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
164
|
+
if uri:
|
165
|
+
try:
|
166
|
+
keypair = Keypair.create_from_uri(uri)
|
167
|
+
except Exception as e:
|
168
|
+
print_error(f"Failed to create keypair from URI {uri}: {str(e)}")
|
169
|
+
wallet.set_coldkey(keypair=keypair, encrypt=False, overwrite=False)
|
170
|
+
wallet.set_coldkeypub(keypair=keypair, encrypt=False, overwrite=False)
|
171
|
+
console.print(
|
172
|
+
f"[dark_sea_green]Coldkey created from URI: {uri}[/dark_sea_green]"
|
173
|
+
)
|
174
|
+
else:
|
175
|
+
wallet.create_new_coldkey(
|
176
|
+
n_words=n_words,
|
177
|
+
use_password=use_password,
|
178
|
+
overwrite=False,
|
179
|
+
)
|
180
|
+
console.print("[dark_sea_green]Coldkey created[/dark_sea_green]")
|
185
181
|
except KeyFileError:
|
186
182
|
print_error("KeyFileError: File is not writable")
|
187
183
|
|
@@ -190,25 +186,40 @@ async def wallet_create(
|
|
190
186
|
wallet: Wallet,
|
191
187
|
n_words: int = 12,
|
192
188
|
use_password: bool = True,
|
189
|
+
uri: Optional[str] = None,
|
193
190
|
):
|
194
191
|
"""Creates a new wallet."""
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
overwrite=False
|
192
|
+
if uri:
|
193
|
+
try:
|
194
|
+
keypair = Keypair.create_from_uri(uri)
|
195
|
+
wallet.set_coldkey(keypair=keypair, encrypt=False, overwrite=False)
|
196
|
+
wallet.set_coldkeypub(keypair=keypair, encrypt=False, overwrite=False)
|
197
|
+
wallet.set_hotkey(keypair=keypair, encrypt=False, overwrite=False)
|
198
|
+
except Exception as e:
|
199
|
+
print_error(f"Failed to create keypair from URI: {str(e)}")
|
200
|
+
console.print(
|
201
|
+
f"[dark_sea_green]Wallet created from URI: {uri}[/dark_sea_green]"
|
200
202
|
)
|
201
|
-
|
202
|
-
|
203
|
+
else:
|
204
|
+
try:
|
205
|
+
wallet.create_new_coldkey(
|
206
|
+
n_words=n_words,
|
207
|
+
use_password=use_password,
|
208
|
+
overwrite=False,
|
209
|
+
)
|
210
|
+
console.print("[dark_sea_green]Coldkey created[/dark_sea_green]")
|
211
|
+
except KeyFileError:
|
212
|
+
print_error("KeyFileError: File is not writable")
|
203
213
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
214
|
+
try:
|
215
|
+
wallet.create_new_hotkey(
|
216
|
+
n_words=n_words,
|
217
|
+
use_password=False,
|
218
|
+
overwrite=False,
|
219
|
+
)
|
220
|
+
console.print("[dark_sea_green]Hotkey created[/dark_sea_green]")
|
221
|
+
except KeyFileError:
|
222
|
+
print_error("KeyFileError: File is not writable")
|
212
223
|
|
213
224
|
|
214
225
|
def get_coldkey_wallets_for_path(path: str) -> list[Wallet]:
|
@@ -252,7 +263,11 @@ async def wallet_balance(
|
|
252
263
|
"""Retrieves the current balance of the specified wallet"""
|
253
264
|
if ss58_addresses:
|
254
265
|
coldkeys = ss58_addresses
|
255
|
-
|
266
|
+
identities = await subtensor.query_all_identities()
|
267
|
+
wallet_names = [
|
268
|
+
f"{identities.get(coldkey, {'name': f'Provided address {i}'})['name']}"
|
269
|
+
for i, coldkey in enumerate(coldkeys)
|
270
|
+
]
|
256
271
|
|
257
272
|
elif not all_balances:
|
258
273
|
if not wallet.coldkeypub_file.exists_on_device():
|
@@ -272,7 +287,7 @@ async def wallet_balance(
|
|
272
287
|
|
273
288
|
block_hash = await subtensor.substrate.get_chain_head()
|
274
289
|
free_balances, staked_balances = await asyncio.gather(
|
275
|
-
subtensor.
|
290
|
+
subtensor.get_balances(*coldkeys, block_hash=block_hash),
|
276
291
|
subtensor.get_total_stake_for_coldkey(*coldkeys, block_hash=block_hash),
|
277
292
|
)
|
278
293
|
|
@@ -292,28 +307,28 @@ async def wallet_balance(
|
|
292
307
|
),
|
293
308
|
Column(
|
294
309
|
"[white]Coldkey Address",
|
295
|
-
style="
|
310
|
+
style=COLOR_PALETTE["GENERAL"]["COLDKEY"],
|
296
311
|
no_wrap=True,
|
297
312
|
),
|
298
313
|
Column(
|
299
314
|
"[white]Free Balance",
|
300
315
|
justify="right",
|
301
|
-
style="
|
316
|
+
style=COLOR_PALETTE["GENERAL"]["BALANCE"],
|
302
317
|
no_wrap=True,
|
303
318
|
),
|
304
319
|
Column(
|
305
320
|
"[white]Staked Balance",
|
306
321
|
justify="right",
|
307
|
-
style="
|
322
|
+
style=COLOR_PALETTE["STAKE"]["STAKE_AMOUNT"],
|
308
323
|
no_wrap=True,
|
309
324
|
),
|
310
325
|
Column(
|
311
326
|
"[white]Total Balance",
|
312
327
|
justify="right",
|
313
|
-
style="
|
328
|
+
style=COLOR_PALETTE["GENERAL"]["BALANCE"],
|
314
329
|
no_wrap=True,
|
315
330
|
),
|
316
|
-
title=f"[
|
331
|
+
title=f"\n [{COLOR_PALETTE['GENERAL']['HEADER']}]Wallet Coldkey Balance\nNetwork: {subtensor.network}",
|
317
332
|
show_footer=True,
|
318
333
|
show_edge=False,
|
319
334
|
border_style="bright_black",
|
@@ -341,6 +356,7 @@ async def wallet_balance(
|
|
341
356
|
)
|
342
357
|
console.print(Padding(table, (0, 0, 0, 4)))
|
343
358
|
await subtensor.substrate.close()
|
359
|
+
return total_free_balance, total_staked_balance
|
344
360
|
|
345
361
|
|
346
362
|
async def get_wallet_transfers(wallet_address: str) -> list[dict]:
|
@@ -535,7 +551,7 @@ async def _get_total_balance(
|
|
535
551
|
]
|
536
552
|
total_balance += sum(
|
537
553
|
(
|
538
|
-
await subtensor.
|
554
|
+
await subtensor.get_balances(
|
539
555
|
*(x.coldkeypub.ss58_address for x in _balance_cold_wallets),
|
540
556
|
block_hash=block_hash,
|
541
557
|
)
|
@@ -557,7 +573,7 @@ async def _get_total_balance(
|
|
557
573
|
):
|
558
574
|
total_balance = sum(
|
559
575
|
(
|
560
|
-
await subtensor.
|
576
|
+
await subtensor.get_balances(
|
561
577
|
coldkey_wallet.coldkeypub.ss58_address, block_hash=block_hash
|
562
578
|
)
|
563
579
|
).values()
|
@@ -581,17 +597,19 @@ async def overview(
|
|
581
597
|
include_hotkeys: Optional[list[str]] = None,
|
582
598
|
exclude_hotkeys: Optional[list[str]] = None,
|
583
599
|
netuids_filter: Optional[list[int]] = None,
|
600
|
+
verbose: bool = False,
|
584
601
|
):
|
585
602
|
"""Prints an overview for the wallet's coldkey."""
|
586
603
|
|
587
604
|
total_balance = Balance(0)
|
588
605
|
|
589
606
|
# We are printing for every coldkey.
|
590
|
-
print_verbose("Fetching total balance for coldkey/s")
|
591
607
|
block_hash = await subtensor.substrate.get_chain_head()
|
592
608
|
all_hotkeys, total_balance = await _get_total_balance(
|
593
609
|
total_balance, subtensor, wallet, all_wallets, block_hash=block_hash
|
594
610
|
)
|
611
|
+
_dynamic_info = await subtensor.all_subnets()
|
612
|
+
dynamic_info = {info.netuid: info for info in _dynamic_info}
|
595
613
|
|
596
614
|
with console.status(
|
597
615
|
f":satellite: Synchronizing with chain [white]{subtensor.network}[/white]",
|
@@ -599,9 +617,6 @@ async def overview(
|
|
599
617
|
) as status:
|
600
618
|
# We are printing for a select number of hotkeys from all_hotkeys.
|
601
619
|
if include_hotkeys or exclude_hotkeys:
|
602
|
-
print_verbose(
|
603
|
-
"Fetching for select hotkeys passed in 'include_hotkeys'", status
|
604
|
-
)
|
605
620
|
all_hotkeys = _get_hotkeys(include_hotkeys, exclude_hotkeys, all_hotkeys)
|
606
621
|
|
607
622
|
# Check we have keys to display.
|
@@ -611,17 +626,14 @@ async def overview(
|
|
611
626
|
|
612
627
|
# Pull neuron info for all keys.
|
613
628
|
neurons: dict[str, list[NeuronInfoLite]] = {}
|
614
|
-
print_verbose("Fetching subnet netuids", status)
|
615
629
|
block, all_netuids = await asyncio.gather(
|
616
630
|
subtensor.substrate.get_block_number(None),
|
617
631
|
subtensor.get_all_subnet_netuids(),
|
618
632
|
)
|
619
633
|
|
620
|
-
print_verbose("Filtering netuids by registered hotkeys", status)
|
621
634
|
netuids = await subtensor.filter_netuids_by_registered_hotkeys(
|
622
635
|
all_netuids, netuids_filter, all_hotkeys, reuse_block=True
|
623
636
|
)
|
624
|
-
# bittensor.logging.debug(f"Netuids to check: {netuids}")
|
625
637
|
|
626
638
|
for netuid in netuids:
|
627
639
|
neurons[str(netuid)] = []
|
@@ -642,122 +654,17 @@ async def overview(
|
|
642
654
|
)
|
643
655
|
all_hotkeys, _ = validate_coldkey_presence(all_hotkeys)
|
644
656
|
|
645
|
-
print_verbose("Fetching key addresses", status)
|
646
657
|
all_hotkey_addresses, hotkey_coldkey_to_hotkey_wallet = _get_key_address(
|
647
658
|
all_hotkeys
|
648
659
|
)
|
649
660
|
|
650
|
-
print_verbose("Pulling and processing neuron information for all keys", status)
|
651
661
|
results = await _get_neurons_for_netuids(
|
652
662
|
subtensor, netuids, all_hotkey_addresses
|
653
663
|
)
|
654
664
|
neurons = _process_neuron_results(results, neurons, netuids)
|
655
|
-
total_coldkey_stake_from_metagraph = await _calculate_total_coldkey_stake(
|
656
|
-
neurons
|
657
|
-
)
|
658
|
-
|
659
|
-
has_alerts = False
|
660
|
-
alerts_table = Table(show_header=True, header_style="bold magenta")
|
661
|
-
alerts_table.add_column("🥩 alert!")
|
662
|
-
alerts_table.add_row(
|
663
|
-
"[bold]Detected the following stake(s) associated with coldkey(s) that are not linked to any local hotkeys:[/bold]"
|
664
|
-
)
|
665
|
-
alerts_table.add_row("")
|
666
|
-
|
667
|
-
coldkeys_to_check = []
|
668
|
-
ck_stakes = await subtensor.get_total_stake_for_coldkey(
|
669
|
-
*(
|
670
|
-
coldkey_wallet.coldkeypub.ss58_address
|
671
|
-
for coldkey_wallet in all_coldkey_wallets
|
672
|
-
if coldkey_wallet.coldkeypub
|
673
|
-
),
|
674
|
-
block_hash=block_hash,
|
675
|
-
)
|
676
|
-
for coldkey_wallet in all_coldkey_wallets:
|
677
|
-
if coldkey_wallet.coldkeypub:
|
678
|
-
# Check if we have any stake with hotkeys that are not registered.
|
679
|
-
difference = (
|
680
|
-
ck_stakes[coldkey_wallet.coldkeypub.ss58_address]
|
681
|
-
- total_coldkey_stake_from_metagraph[
|
682
|
-
coldkey_wallet.coldkeypub.ss58_address
|
683
|
-
]
|
684
|
-
)
|
685
|
-
if difference == 0:
|
686
|
-
continue # We have all our stake registered.
|
687
|
-
|
688
|
-
has_alerts = True
|
689
|
-
coldkeys_to_check.append(coldkey_wallet)
|
690
|
-
alerts_table.add_row(
|
691
|
-
"[light_goldenrod2]{}[/light_goldenrod2] stake associated with coldkey [bright_magenta]{}[/bright_magenta] (ss58: [bright_magenta]{}[/bright_magenta])".format(
|
692
|
-
abs(difference),
|
693
|
-
coldkey_wallet.name,
|
694
|
-
coldkey_wallet.coldkeypub.ss58_address,
|
695
|
-
)
|
696
|
-
)
|
697
|
-
if has_alerts:
|
698
|
-
alerts_table.add_row("")
|
699
|
-
alerts_table.add_row(
|
700
|
-
"[bold yellow]Note:[/bold yellow] This stake might be delegated, staked to another user's hotkey, or associated with a hotkey not present in your wallet."
|
701
|
-
)
|
702
|
-
alerts_table.add_row(
|
703
|
-
"You can find out more by executing `[bold]btcli wallet inspect[/bold]` command."
|
704
|
-
)
|
705
|
-
|
706
|
-
if coldkeys_to_check:
|
707
|
-
# We have some stake that is not with a registered hotkey.
|
708
|
-
if "-1" not in neurons:
|
709
|
-
neurons["-1"] = []
|
710
|
-
|
711
|
-
print_verbose("Checking coldkeys for de-registered stake", status)
|
712
|
-
results = await asyncio.gather(
|
713
|
-
*[
|
714
|
-
_get_de_registered_stake_for_coldkey_wallet(
|
715
|
-
subtensor, all_hotkey_addresses, coldkey_wallet
|
716
|
-
)
|
717
|
-
for coldkey_wallet in coldkeys_to_check
|
718
|
-
]
|
719
|
-
)
|
720
|
-
|
721
|
-
for result in results:
|
722
|
-
coldkey_wallet, de_registered_stake, err_msg = result
|
723
|
-
if err_msg is not None:
|
724
|
-
err_console.print(err_msg)
|
725
|
-
|
726
|
-
if len(de_registered_stake) == 0:
|
727
|
-
continue # We have no de-registered stake with this coldkey.
|
728
|
-
|
729
|
-
de_registered_neurons = []
|
730
|
-
for hotkey_addr, our_stake in de_registered_stake:
|
731
|
-
# Make a neuron info lite for this hotkey and coldkey.
|
732
|
-
de_registered_neuron = NeuronInfoLite.get_null_neuron()
|
733
|
-
de_registered_neuron.hotkey = hotkey_addr
|
734
|
-
de_registered_neuron.coldkey = coldkey_wallet.coldkeypub.ss58_address
|
735
|
-
de_registered_neuron.total_stake = Balance(our_stake)
|
736
|
-
de_registered_neurons.append(de_registered_neuron)
|
737
|
-
|
738
|
-
# Add this hotkey to the wallets dict
|
739
|
-
wallet_ = WalletLike(
|
740
|
-
name=wallet.name,
|
741
|
-
hotkey_ss58=hotkey_addr,
|
742
|
-
hotkey_str=hotkey_addr[:5],
|
743
|
-
)
|
744
|
-
# Indicates a hotkey not on local machine but exists in stake_info obj on-chain
|
745
|
-
if hotkey_coldkey_to_hotkey_wallet.get(hotkey_addr) is None:
|
746
|
-
hotkey_coldkey_to_hotkey_wallet[hotkey_addr] = {}
|
747
|
-
hotkey_coldkey_to_hotkey_wallet[hotkey_addr][
|
748
|
-
coldkey_wallet.coldkeypub.ss58_address
|
749
|
-
] = wallet_
|
750
|
-
|
751
|
-
# Add neurons to overview.
|
752
|
-
neurons["-1"].extend(de_registered_neurons)
|
753
|
-
|
754
665
|
# Setup outer table.
|
755
666
|
grid = Table.grid(pad_edge=True)
|
756
667
|
|
757
|
-
# If there are any alerts, add them to the grid
|
758
|
-
if has_alerts:
|
759
|
-
grid.add_row(alerts_table)
|
760
|
-
|
761
668
|
# Add title
|
762
669
|
if not all_wallets:
|
763
670
|
title = "[underline dark_orange]Wallet[/underline dark_orange]\n"
|
@@ -776,9 +683,6 @@ async def overview(
|
|
776
683
|
)
|
777
684
|
)
|
778
685
|
# Generate rows per netuid
|
779
|
-
hotkeys_seen = set()
|
780
|
-
total_neurons = 0
|
781
|
-
total_stake = 0.0
|
782
686
|
tempos = await asyncio.gather(
|
783
687
|
*[
|
784
688
|
subtensor.get_hyperparameter("Tempo", netuid, block_hash)
|
@@ -786,7 +690,6 @@ async def overview(
|
|
786
690
|
]
|
787
691
|
)
|
788
692
|
for netuid, subnet_tempo in zip(netuids, tempos):
|
789
|
-
last_subnet = netuid == netuids[-1]
|
790
693
|
table_data = []
|
791
694
|
total_rank = 0.0
|
792
695
|
total_trust = 0.0
|
@@ -795,6 +698,8 @@ async def overview(
|
|
795
698
|
total_incentive = 0.0
|
796
699
|
total_dividends = 0.0
|
797
700
|
total_emission = 0
|
701
|
+
total_stake = 0
|
702
|
+
total_neurons = 0
|
798
703
|
|
799
704
|
for nn in neurons[str(netuid)]:
|
800
705
|
hotwallet = hotkey_coldkey_to_hotkey_wallet.get(nn.hotkey, {}).get(
|
@@ -815,7 +720,7 @@ async def overview(
|
|
815
720
|
validator_trust = nn.validator_trust
|
816
721
|
incentive = nn.incentive
|
817
722
|
dividends = nn.dividends
|
818
|
-
emission = int(nn.emission / (subnet_tempo + 1) * 1e9)
|
723
|
+
emission = int(nn.emission / (subnet_tempo + 1) * 1e9) # Per block
|
819
724
|
last_update = int(block - nn.last_update)
|
820
725
|
validator_permit = nn.validator_permit
|
821
726
|
row = [
|
@@ -823,14 +728,14 @@ async def overview(
|
|
823
728
|
hotwallet.hotkey_str,
|
824
729
|
str(uid),
|
825
730
|
str(active),
|
826
|
-
"{:.
|
827
|
-
"{:.
|
828
|
-
"{:.
|
829
|
-
"{:.
|
830
|
-
"{:.
|
831
|
-
"{:.
|
832
|
-
"{
|
833
|
-
"{:.
|
731
|
+
f"{stake:.4f}" if verbose else millify_tao(stake),
|
732
|
+
f"{rank:.4f}" if verbose else millify_tao(rank),
|
733
|
+
f"{trust:.4f}" if verbose else millify_tao(trust),
|
734
|
+
f"{consensus:.4f}" if verbose else millify_tao(consensus),
|
735
|
+
f"{incentive:.4f}" if verbose else millify_tao(incentive),
|
736
|
+
f"{dividends:.4f}" if verbose else millify_tao(dividends),
|
737
|
+
f"{emission:.4f}",
|
738
|
+
f"{validator_trust:.4f}" if verbose else millify_tao(validator_trust),
|
834
739
|
"*" if validator_permit else "",
|
835
740
|
str(last_update),
|
836
741
|
(
|
@@ -848,23 +753,15 @@ async def overview(
|
|
848
753
|
total_dividends += dividends
|
849
754
|
total_emission += emission
|
850
755
|
total_validator_trust += validator_trust
|
851
|
-
|
852
|
-
|
853
|
-
# Don't double count stake on hotkey-coldkey pairs.
|
854
|
-
hotkeys_seen.add((nn.hotkey, nn.coldkey))
|
855
|
-
total_stake += stake
|
856
|
-
|
857
|
-
# netuid -1 are neurons that are de-registered.
|
858
|
-
if netuid != "-1":
|
859
|
-
total_neurons += 1
|
756
|
+
total_stake += stake
|
757
|
+
total_neurons += 1
|
860
758
|
|
861
759
|
table_data.append(row)
|
862
760
|
|
863
761
|
# Add subnet header
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
grid.add_row(f"Subnet: [dark_orange]{netuid}[/dark_orange]")
|
762
|
+
grid.add_row(
|
763
|
+
f"Subnet: [dark_orange]{netuid}: {get_subnet_name(dynamic_info[netuid])} {dynamic_info[netuid].symbol}[/dark_orange]"
|
764
|
+
)
|
868
765
|
width = console.width
|
869
766
|
table = Table(
|
870
767
|
show_footer=False,
|
@@ -873,45 +770,34 @@ async def overview(
|
|
873
770
|
expand=True,
|
874
771
|
width=width - 5,
|
875
772
|
)
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
)
|
880
|
-
table.add_column(
|
881
|
-
"[white]HOTKEY", str(total_neurons), style="bright_cyan", ratio=2
|
882
|
-
)
|
883
|
-
else:
|
884
|
-
# No footer for non-last subnet.
|
885
|
-
table.add_column("[white]COLDKEY", style="bold bright_cyan", ratio=2)
|
886
|
-
table.add_column("[white]HOTKEY", style="bright_cyan", ratio=2)
|
773
|
+
|
774
|
+
table.add_column("[white]COLDKEY", style="bold bright_cyan", ratio=2)
|
775
|
+
table.add_column("[white]HOTKEY", style="bright_cyan", ratio=2)
|
887
776
|
table.add_column(
|
888
777
|
"[white]UID", str(total_neurons), style="rgb(42,161,152)", ratio=1
|
889
778
|
)
|
890
779
|
table.add_column(
|
891
780
|
"[white]ACTIVE", justify="right", style="#8787ff", no_wrap=True, ratio=1
|
892
781
|
)
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
no_wrap=True,
|
910
|
-
ratio=1.5,
|
911
|
-
)
|
782
|
+
|
783
|
+
_total_stake_formatted = (
|
784
|
+
f"{total_stake:.4f}" if verbose else millify_tao(total_stake)
|
785
|
+
)
|
786
|
+
table.add_column(
|
787
|
+
"[white]STAKE(\u03c4)"
|
788
|
+
if netuid == 0
|
789
|
+
else f"[white]STAKE({Balance.get_unit(netuid)})",
|
790
|
+
f"{_total_stake_formatted} {Balance.get_unit(netuid)}"
|
791
|
+
if netuid != 0
|
792
|
+
else f"{Balance.get_unit(netuid)} {_total_stake_formatted}",
|
793
|
+
justify="right",
|
794
|
+
style="dark_orange",
|
795
|
+
no_wrap=True,
|
796
|
+
ratio=1.5,
|
797
|
+
)
|
912
798
|
table.add_column(
|
913
799
|
"[white]RANK",
|
914
|
-
"{:.
|
800
|
+
f"{total_rank:.4f}",
|
915
801
|
justify="right",
|
916
802
|
style="medium_purple",
|
917
803
|
no_wrap=True,
|
@@ -919,7 +805,7 @@ async def overview(
|
|
919
805
|
)
|
920
806
|
table.add_column(
|
921
807
|
"[white]TRUST",
|
922
|
-
"{:.
|
808
|
+
f"{total_trust:.4f}",
|
923
809
|
justify="right",
|
924
810
|
style="green",
|
925
811
|
no_wrap=True,
|
@@ -927,7 +813,7 @@ async def overview(
|
|
927
813
|
)
|
928
814
|
table.add_column(
|
929
815
|
"[white]CONSENSUS",
|
930
|
-
"{:.
|
816
|
+
f"{total_consensus:.4f}",
|
931
817
|
justify="right",
|
932
818
|
style="rgb(42,161,152)",
|
933
819
|
no_wrap=True,
|
@@ -935,7 +821,7 @@ async def overview(
|
|
935
821
|
)
|
936
822
|
table.add_column(
|
937
823
|
"[white]INCENTIVE",
|
938
|
-
"{:.
|
824
|
+
f"{total_incentive:.4f}",
|
939
825
|
justify="right",
|
940
826
|
style="#5fd7ff",
|
941
827
|
no_wrap=True,
|
@@ -943,7 +829,7 @@ async def overview(
|
|
943
829
|
)
|
944
830
|
table.add_column(
|
945
831
|
"[white]DIVIDENDS",
|
946
|
-
"{:.
|
832
|
+
f"{total_dividends:.4f}",
|
947
833
|
justify="right",
|
948
834
|
style="#8787d7",
|
949
835
|
no_wrap=True,
|
@@ -951,7 +837,7 @@ async def overview(
|
|
951
837
|
)
|
952
838
|
table.add_column(
|
953
839
|
"[white]EMISSION(\u03c1)",
|
954
|
-
"\u03c1{
|
840
|
+
f"\u03c1{total_emission}",
|
955
841
|
justify="right",
|
956
842
|
style="#d7d7ff",
|
957
843
|
no_wrap=True,
|
@@ -959,7 +845,7 @@ async def overview(
|
|
959
845
|
)
|
960
846
|
table.add_column(
|
961
847
|
"[white]VTRUST",
|
962
|
-
"{:.
|
848
|
+
f"{total_validator_trust:.4f}",
|
963
849
|
justify="right",
|
964
850
|
style="magenta",
|
965
851
|
no_wrap=True,
|
@@ -1142,7 +1028,7 @@ def _map_hotkey_to_neurons(
|
|
1142
1028
|
|
1143
1029
|
async def _fetch_neuron_for_netuid(
|
1144
1030
|
netuid: int, subtensor: SubtensorInterface
|
1145
|
-
) -> tuple[int,
|
1031
|
+
) -> tuple[int, list[NeuronInfoLite]]:
|
1146
1032
|
"""
|
1147
1033
|
Retrieves all neurons for a specified netuid
|
1148
1034
|
|
@@ -1151,25 +1037,13 @@ async def _fetch_neuron_for_netuid(
|
|
1151
1037
|
|
1152
1038
|
:return: the original netuid, and a mapping of the neurons to their NeuronInfoLite objects
|
1153
1039
|
"""
|
1154
|
-
|
1155
|
-
async def neurons_lite_for_uid(uid: int) -> Optional[str]:
|
1156
|
-
block_hash = subtensor.substrate.last_block_hash
|
1157
|
-
hex_bytes_result = await subtensor.query_runtime_api(
|
1158
|
-
runtime_api="NeuronInfoRuntimeApi",
|
1159
|
-
method="get_neurons_lite",
|
1160
|
-
params=[uid],
|
1161
|
-
block_hash=block_hash,
|
1162
|
-
)
|
1163
|
-
|
1164
|
-
return hex_bytes_result
|
1165
|
-
|
1166
|
-
neurons = await neurons_lite_for_uid(uid=netuid)
|
1040
|
+
neurons = await subtensor.neurons_lite(netuid=netuid)
|
1167
1041
|
return netuid, neurons
|
1168
1042
|
|
1169
1043
|
|
1170
1044
|
async def _fetch_all_neurons(
|
1171
1045
|
netuids: list[int], subtensor
|
1172
|
-
) -> list[tuple[int,
|
1046
|
+
) -> list[tuple[int, list[NeuronInfoLite]]]:
|
1173
1047
|
"""Retrieves all neurons for each of the specified netuids"""
|
1174
1048
|
return list(
|
1175
1049
|
await asyncio.gather(
|
@@ -1178,101 +1052,26 @@ async def _fetch_all_neurons(
|
|
1178
1052
|
)
|
1179
1053
|
|
1180
1054
|
|
1181
|
-
def _process_neurons_for_netuids(
|
1182
|
-
netuids_with_all_neurons_hex_bytes: list[tuple[int, Optional[str]]],
|
1183
|
-
) -> list[tuple[int, list[NeuronInfoLite]]]:
|
1184
|
-
"""
|
1185
|
-
Decode a list of hex-bytes neurons with their respective netuid
|
1186
|
-
|
1187
|
-
:param netuids_with_all_neurons_hex_bytes: netuids with hex-bytes neurons
|
1188
|
-
:return: netuids mapped to decoded neurons
|
1189
|
-
"""
|
1190
|
-
all_results = [
|
1191
|
-
(netuid, NeuronInfoLite.list_from_vec_u8(hex_to_bytes(result)))
|
1192
|
-
if result
|
1193
|
-
else (netuid, [])
|
1194
|
-
for netuid, result in netuids_with_all_neurons_hex_bytes
|
1195
|
-
]
|
1196
|
-
return all_results
|
1197
|
-
|
1198
|
-
|
1199
1055
|
async def _get_neurons_for_netuids(
|
1200
1056
|
subtensor: SubtensorInterface, netuids: list[int], hot_wallets: list[str]
|
1201
1057
|
) -> list[tuple[int, list["NeuronInfoLite"], Optional[str]]]:
|
1202
|
-
|
1203
|
-
|
1204
|
-
all_processed_neurons = _process_neurons_for_netuids(all_neurons_hex_bytes)
|
1058
|
+
all_neurons = await _fetch_all_neurons(netuids, subtensor)
|
1205
1059
|
return [
|
1206
1060
|
_map_hotkey_to_neurons(neurons, hot_wallets, netuid)
|
1207
|
-
for netuid, neurons in
|
1061
|
+
for netuid, neurons in all_neurons
|
1208
1062
|
]
|
1209
1063
|
|
1210
1064
|
|
1211
|
-
async def _get_de_registered_stake_for_coldkey_wallet(
|
1212
|
-
subtensor: SubtensorInterface,
|
1213
|
-
all_hotkey_addresses: Collection[str],
|
1214
|
-
coldkey_wallet: Wallet,
|
1215
|
-
) -> tuple[Wallet, list[tuple[str, float]], Optional[str]]:
|
1216
|
-
"""
|
1217
|
-
Looks at the total stake of a coldkey, then filters this based on the supplied hotkey addresses
|
1218
|
-
depending on whether the hotkey is a delegate
|
1219
|
-
|
1220
|
-
:param subtensor: SubtensorInterface to make queries with
|
1221
|
-
:param all_hotkey_addresses: collection of hotkey SS58 addresses
|
1222
|
-
:param coldkey_wallet: Wallet containing coldkey
|
1223
|
-
|
1224
|
-
:return: (original wallet, [(hotkey SS58, stake in TAO), ...], error message)
|
1225
|
-
"""
|
1226
|
-
# Pull all stake for our coldkey
|
1227
|
-
all_stake_info_for_coldkey = await subtensor.get_stake_info_for_coldkey(
|
1228
|
-
coldkey_ss58=coldkey_wallet.coldkeypub.ss58_address, reuse_block=True
|
1229
|
-
)
|
1230
|
-
|
1231
|
-
# Filter out hotkeys that are in our wallets
|
1232
|
-
# Filter out hotkeys that are delegates.
|
1233
|
-
async def _filter_stake_info(stake_info: StakeInfo) -> bool:
|
1234
|
-
if stake_info.stake == 0:
|
1235
|
-
return False # Skip hotkeys that we have no stake with.
|
1236
|
-
if stake_info.hotkey_ss58 in all_hotkey_addresses:
|
1237
|
-
return False # Skip hotkeys that are in our wallets.
|
1238
|
-
return not await subtensor.is_hotkey_delegate(
|
1239
|
-
hotkey_ss58=stake_info.hotkey_ss58, reuse_block=True
|
1240
|
-
)
|
1241
|
-
|
1242
|
-
all_staked = await asyncio.gather(
|
1243
|
-
*[_filter_stake_info(stake_info) for stake_info in all_stake_info_for_coldkey]
|
1244
|
-
)
|
1245
|
-
|
1246
|
-
# Collecting all filtered stake info using async for loop
|
1247
|
-
all_staked_hotkeys = []
|
1248
|
-
for stake_info, staked in zip(all_stake_info_for_coldkey, all_staked):
|
1249
|
-
if staked:
|
1250
|
-
all_staked_hotkeys.append(
|
1251
|
-
(
|
1252
|
-
stake_info.hotkey_ss58,
|
1253
|
-
stake_info.stake.tao,
|
1254
|
-
)
|
1255
|
-
)
|
1256
|
-
|
1257
|
-
return coldkey_wallet, all_staked_hotkeys, None
|
1258
|
-
|
1259
|
-
|
1260
1065
|
async def transfer(
|
1261
1066
|
wallet: Wallet,
|
1262
1067
|
subtensor: SubtensorInterface,
|
1263
1068
|
destination: str,
|
1264
1069
|
amount: float,
|
1265
|
-
transfer_all: bool,
|
1266
1070
|
prompt: bool,
|
1267
1071
|
):
|
1268
1072
|
"""Transfer token of amount to destination."""
|
1269
1073
|
await transfer_extrinsic(
|
1270
|
-
subtensor,
|
1271
|
-
wallet,
|
1272
|
-
destination,
|
1273
|
-
Balance.from_tao(amount),
|
1274
|
-
transfer_all,
|
1275
|
-
prompt=prompt,
|
1074
|
+
subtensor, wallet, destination, Balance.from_tao(amount), prompt=prompt
|
1276
1075
|
)
|
1277
1076
|
|
1278
1077
|
|
@@ -1286,6 +1085,8 @@ async def inspect(
|
|
1286
1085
|
delegates_: list[tuple[DelegateInfo, Balance]],
|
1287
1086
|
) -> Generator[list[str], None, None]:
|
1288
1087
|
for d_, staked in delegates_:
|
1088
|
+
if not staked.tao > 0:
|
1089
|
+
continue
|
1289
1090
|
if d_.hotkey_ss58 in registered_delegate_info:
|
1290
1091
|
delegate_name = registered_delegate_info[d_.hotkey_ss58].display
|
1291
1092
|
else:
|
@@ -1295,7 +1096,11 @@ async def inspect(
|
|
1295
1096
|
+ [
|
1296
1097
|
str(delegate_name),
|
1297
1098
|
str(staked),
|
1298
|
-
str(
|
1099
|
+
str(
|
1100
|
+
d_.total_daily_return.tao * (staked.tao / d_.total_stake.tao)
|
1101
|
+
if d_.total_stake.tao != 0
|
1102
|
+
else 0
|
1103
|
+
),
|
1299
1104
|
]
|
1300
1105
|
+ [""] * 4
|
1301
1106
|
)
|
@@ -1375,7 +1180,7 @@ async def inspect(
|
|
1375
1180
|
all_delegates: list[list[tuple[DelegateInfo, Balance]]]
|
1376
1181
|
with console.status("Pulling balance data...", spinner="aesthetic"):
|
1377
1182
|
balances, all_neurons, all_delegates = await asyncio.gather(
|
1378
|
-
subtensor.
|
1183
|
+
subtensor.get_balances(
|
1379
1184
|
*[w.coldkeypub.ss58_address for w in wallets_with_ckp_file],
|
1380
1185
|
block_hash=block_hash,
|
1381
1186
|
),
|
@@ -1426,14 +1231,13 @@ async def faucet(
|
|
1426
1231
|
output_in_place: bool,
|
1427
1232
|
log_verbose: bool,
|
1428
1233
|
max_successes: int = 3,
|
1429
|
-
prompt: bool = True,
|
1430
1234
|
):
|
1431
1235
|
# TODO: - work out prompts to be passed through the cli
|
1432
1236
|
success = await run_faucet_extrinsic(
|
1433
1237
|
subtensor,
|
1434
1238
|
wallet,
|
1435
1239
|
tpb=threads_per_block,
|
1436
|
-
prompt=
|
1240
|
+
prompt=False,
|
1437
1241
|
update_interval=update_interval,
|
1438
1242
|
num_processes=processes,
|
1439
1243
|
cuda=use_cuda,
|
@@ -1461,210 +1265,97 @@ async def swap_hotkey(
|
|
1461
1265
|
)
|
1462
1266
|
|
1463
1267
|
|
1464
|
-
def
|
1465
|
-
|
1466
|
-
|
1467
|
-
"""
|
1468
|
-
Used to prompt the user to input their info for setting the ID
|
1469
|
-
:return: (display_name, legal_name, web_url, riot_handle, email,pgp_fingerprint, image_url, info_, twitter_url,
|
1470
|
-
validator_id)
|
1471
|
-
"""
|
1472
|
-
text_rejection = partial(
|
1473
|
-
retry_prompt,
|
1474
|
-
rejection=lambda x: sys.getsizeof(x) > 113,
|
1475
|
-
rejection_text="[red]Error:[/red] Identity field must be <= 64 raw bytes.",
|
1476
|
-
)
|
1268
|
+
def create_identity_table(title: str = None):
|
1269
|
+
if not title:
|
1270
|
+
title = "On-Chain Identity"
|
1477
1271
|
|
1478
|
-
|
1479
|
-
|
1480
|
-
|
1481
|
-
|
1482
|
-
|
1483
|
-
|
1484
|
-
|
1485
|
-
|
1486
|
-
|
1487
|
-
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1491
|
-
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
"[red]Error:[/red] PGP Fingerprint must be exactly 20 bytes.",
|
1496
|
-
)
|
1497
|
-
image_url = text_rejection("Image URL")
|
1498
|
-
info_ = text_rejection("Enter info")
|
1499
|
-
twitter_url = text_rejection("𝕏 (Twitter) URL")
|
1500
|
-
|
1501
|
-
subnet_netuid = None
|
1502
|
-
if validator is False:
|
1503
|
-
subnet_netuid = IntPrompt.ask("Enter the netuid of the subnet you own")
|
1504
|
-
|
1505
|
-
return (
|
1506
|
-
display_name,
|
1507
|
-
legal_name,
|
1508
|
-
web_url,
|
1509
|
-
pgp_fingerprint,
|
1510
|
-
riot_handle,
|
1511
|
-
email,
|
1512
|
-
image_url,
|
1513
|
-
twitter_url,
|
1514
|
-
info_,
|
1515
|
-
validator,
|
1516
|
-
subnet_netuid,
|
1272
|
+
table = Table(
|
1273
|
+
Column(
|
1274
|
+
"Item",
|
1275
|
+
justify="right",
|
1276
|
+
style=COLOR_PALETTE["GENERAL"]["SUBHEADING_MAIN"],
|
1277
|
+
no_wrap=True,
|
1278
|
+
),
|
1279
|
+
Column("Value", style=COLOR_PALETTE["GENERAL"]["SUBHEADING"]),
|
1280
|
+
title=f"\n[{COLOR_PALETTE['GENERAL']['HEADER']}]{title}",
|
1281
|
+
show_footer=True,
|
1282
|
+
show_edge=False,
|
1283
|
+
header_style="bold white",
|
1284
|
+
border_style="bright_black",
|
1285
|
+
style="bold",
|
1286
|
+
title_justify="center",
|
1287
|
+
show_lines=False,
|
1288
|
+
pad_edge=True,
|
1517
1289
|
)
|
1290
|
+
return table
|
1518
1291
|
|
1519
1292
|
|
1520
1293
|
async def set_id(
|
1521
1294
|
wallet: Wallet,
|
1522
1295
|
subtensor: SubtensorInterface,
|
1523
|
-
|
1524
|
-
legal_name: str,
|
1296
|
+
name: str,
|
1525
1297
|
web_url: str,
|
1526
|
-
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
info_: str,
|
1532
|
-
validator_id: bool,
|
1533
|
-
subnet_netuid: int,
|
1298
|
+
image_url: str,
|
1299
|
+
discord: str,
|
1300
|
+
description: str,
|
1301
|
+
additional: str,
|
1302
|
+
github_repo: str,
|
1534
1303
|
prompt: bool,
|
1535
1304
|
):
|
1536
1305
|
"""Create a new or update existing identity on-chain."""
|
1537
1306
|
|
1538
|
-
|
1539
|
-
"
|
1540
|
-
"
|
1541
|
-
"
|
1542
|
-
"
|
1543
|
-
"
|
1544
|
-
"
|
1545
|
-
"
|
1546
|
-
"image": image,
|
1547
|
-
"twitter": twitter,
|
1548
|
-
"info": info_,
|
1307
|
+
identity_data = {
|
1308
|
+
"name": name.encode(),
|
1309
|
+
"url": web_url.encode(),
|
1310
|
+
"image": image_url.encode(),
|
1311
|
+
"discord": discord.encode(),
|
1312
|
+
"description": description.encode(),
|
1313
|
+
"additional": additional.encode(),
|
1314
|
+
"github_repo": github_repo.encode(),
|
1549
1315
|
}
|
1550
1316
|
|
1551
|
-
|
1552
|
-
|
1553
|
-
|
1554
|
-
print_error(f"The PGP is not in the correct format: {e}")
|
1555
|
-
raise typer.Exit()
|
1556
|
-
|
1557
|
-
for field, string in id_dict.items():
|
1558
|
-
if (
|
1559
|
-
field == "pgp_fingerprint"
|
1560
|
-
and pgp_fingerprint
|
1561
|
-
and len(pgp_fingerprint_encoded) != 20
|
1562
|
-
):
|
1317
|
+
for field, value in identity_data.items():
|
1318
|
+
max_size = 64 # bytes
|
1319
|
+
if len(value) > max_size:
|
1563
1320
|
err_console.print(
|
1564
|
-
"[red]Error:[/red]
|
1321
|
+
f"[red]Error:[/red] Identity field [white]{field}[/white] must be <= {max_size} bytes.\n"
|
1322
|
+
f"Value '{value.decode()}' is {len(value)} bytes."
|
1565
1323
|
)
|
1566
1324
|
return False
|
1567
|
-
elif (size := getsizeof(string)) > 113: # 64 + 49 overhead bytes for string
|
1568
|
-
err_console.print(
|
1569
|
-
f"[red]Error:[/red] Identity field [white]{field}[/white] must be <= 64 raw bytes.\n"
|
1570
|
-
f"Value: '{string}' currently [white]{size} bytes[/white]."
|
1571
|
-
)
|
1572
|
-
return False
|
1573
|
-
|
1574
|
-
identified = (
|
1575
|
-
wallet.hotkey.ss58_address if validator_id else wallet.coldkey.ss58_address
|
1576
|
-
)
|
1577
|
-
encoded_id_dict = {
|
1578
|
-
"info": {
|
1579
|
-
"additional": [[]],
|
1580
|
-
"display": {f"Raw{len(display_name.encode())}": display_name.encode()},
|
1581
|
-
"legal": {f"Raw{len(legal_name.encode())}": legal_name.encode()},
|
1582
|
-
"web": {f"Raw{len(web_url.encode())}": web_url.encode()},
|
1583
|
-
"riot": {f"Raw{len(riot_handle.encode())}": riot_handle.encode()},
|
1584
|
-
"email": {f"Raw{len(email.encode())}": email.encode()},
|
1585
|
-
"pgp_fingerprint": pgp_fingerprint_encoded if pgp_fingerprint else None,
|
1586
|
-
"image": {f"Raw{len(image.encode())}": image.encode()},
|
1587
|
-
"info": {f"Raw{len(info_.encode())}": info_.encode()},
|
1588
|
-
"twitter": {f"Raw{len(twitter.encode())}": twitter.encode()},
|
1589
|
-
},
|
1590
|
-
"identified": identified,
|
1591
|
-
}
|
1592
|
-
|
1593
|
-
if prompt:
|
1594
|
-
if not Confirm.ask(
|
1595
|
-
"Cost to register an Identity is [bold white italic]0.1 Tao[/bold white italic],"
|
1596
|
-
" are you sure you wish to continue?"
|
1597
|
-
):
|
1598
|
-
console.print(":cross_mark: Aborted!")
|
1599
|
-
raise typer.Exit()
|
1600
|
-
|
1601
|
-
if validator_id:
|
1602
|
-
block_hash = await subtensor.substrate.get_chain_head()
|
1603
|
-
|
1604
|
-
is_registered_on_root, hotkey_owner = await asyncio.gather(
|
1605
|
-
is_hotkey_registered(
|
1606
|
-
subtensor, netuid=0, hotkey_ss58=wallet.hotkey.ss58_address
|
1607
|
-
),
|
1608
|
-
subtensor.get_hotkey_owner(
|
1609
|
-
hotkey_ss58=wallet.hotkey.ss58_address, block_hash=block_hash
|
1610
|
-
),
|
1611
|
-
)
|
1612
|
-
|
1613
|
-
if not is_registered_on_root:
|
1614
|
-
print_error("The hotkey is not registered on root. Aborting.")
|
1615
|
-
return False
|
1616
|
-
|
1617
|
-
own_hotkey = wallet.coldkeypub.ss58_address == hotkey_owner
|
1618
|
-
if not own_hotkey:
|
1619
|
-
print_error("The hotkey doesn't belong to the coldkey wallet. Aborting.")
|
1620
|
-
return False
|
1621
|
-
else:
|
1622
|
-
subnet_owner_ = await subtensor.substrate.query(
|
1623
|
-
module="SubtensorModule",
|
1624
|
-
storage_function="SubnetOwner",
|
1625
|
-
params=[subnet_netuid],
|
1626
|
-
)
|
1627
|
-
subnet_owner = decode_account_id(subnet_owner_[0])
|
1628
|
-
if subnet_owner != wallet.coldkeypub.ss58_address:
|
1629
|
-
print_error(f":cross_mark: This wallet doesn't own subnet {subnet_netuid}.")
|
1630
|
-
return False
|
1631
1325
|
|
1632
|
-
|
1326
|
+
try:
|
1327
|
+
wallet.unlock_coldkey()
|
1328
|
+
except KeyFileError:
|
1329
|
+
err_console.print("Error decrypting coldkey (possibly incorrect password)")
|
1633
1330
|
return False
|
1634
1331
|
|
1332
|
+
call = await subtensor.substrate.compose_call(
|
1333
|
+
call_module="SubtensorModule",
|
1334
|
+
call_function="set_identity",
|
1335
|
+
call_params=identity_data,
|
1336
|
+
)
|
1337
|
+
|
1635
1338
|
with console.status(
|
1636
|
-
":satellite: [
|
1339
|
+
" :satellite: [dark_sea_green3]Updating identity on-chain...", spinner="earth"
|
1637
1340
|
):
|
1638
|
-
call = await subtensor.substrate.compose_call(
|
1639
|
-
call_module="Registry",
|
1640
|
-
call_function="set_identity",
|
1641
|
-
call_params=encoded_id_dict,
|
1642
|
-
)
|
1643
1341
|
success, err_msg = await subtensor.sign_and_send_extrinsic(call, wallet)
|
1644
1342
|
|
1645
1343
|
if not success:
|
1646
1344
|
err_console.print(f"[red]:cross_mark: Failed![/red] {err_msg}")
|
1647
1345
|
return
|
1648
1346
|
|
1649
|
-
console.print(":white_heavy_check_mark: Success!")
|
1650
|
-
identity = await subtensor.query_identity(
|
1651
|
-
identified or wallet.coldkey.ss58_address
|
1652
|
-
)
|
1653
|
-
|
1654
|
-
table = Table(
|
1655
|
-
Column("Key", justify="right", style="cyan", no_wrap=True),
|
1656
|
-
Column("Value", style="magenta"),
|
1657
|
-
title="[bold white italic]Updated On-Chain Identity",
|
1658
|
-
)
|
1347
|
+
console.print(":white_heavy_check_mark: [dark_sea_green3]Success!")
|
1348
|
+
identity = await subtensor.query_identity(wallet.coldkeypub.ss58_address)
|
1659
1349
|
|
1660
|
-
table
|
1350
|
+
table = create_identity_table(title="New on-chain Identity")
|
1351
|
+
table.add_row("Address", wallet.coldkeypub.ss58_address)
|
1661
1352
|
for key, value in identity.items():
|
1662
|
-
table.add_row(key, str(value) if value
|
1353
|
+
table.add_row(key, str(value) if value else "~")
|
1663
1354
|
|
1664
1355
|
return console.print(table)
|
1665
1356
|
|
1666
1357
|
|
1667
|
-
async def get_id(subtensor: SubtensorInterface, ss58_address: str):
|
1358
|
+
async def get_id(subtensor: SubtensorInterface, ss58_address: str, title: str = None):
|
1668
1359
|
with console.status(
|
1669
1360
|
":satellite: [bold green]Querying chain identity...", spinner="earth"
|
1670
1361
|
):
|
@@ -1672,40 +1363,37 @@ async def get_id(subtensor: SubtensorInterface, ss58_address: str):
|
|
1672
1363
|
|
1673
1364
|
if not identity:
|
1674
1365
|
err_console.print(
|
1675
|
-
f"[
|
1676
|
-
f" for [
|
1677
|
-
f" on
|
1366
|
+
f"[blue]Existing identity not found[/blue]"
|
1367
|
+
f" for [{COLOR_PALETTE['GENERAL']['COLDKEY']}]{ss58_address}[/{COLOR_PALETTE['GENERAL']['COLDKEY']}]"
|
1368
|
+
f" on {subtensor}"
|
1678
1369
|
)
|
1679
|
-
return
|
1680
|
-
table = Table(
|
1681
|
-
Column("Item", justify="right", style="cyan", no_wrap=True),
|
1682
|
-
Column("Value", style="magenta"),
|
1683
|
-
title="[bold white italic]On-Chain Identity",
|
1684
|
-
)
|
1370
|
+
return {}
|
1685
1371
|
|
1372
|
+
table = create_identity_table(title)
|
1686
1373
|
table.add_row("Address", ss58_address)
|
1687
1374
|
for key, value in identity.items():
|
1688
|
-
table.add_row(key, str(value) if value
|
1375
|
+
table.add_row(key, str(value) if value else "~")
|
1689
1376
|
|
1690
|
-
|
1377
|
+
console.print(table)
|
1378
|
+
return identity
|
1691
1379
|
|
1692
1380
|
|
1693
1381
|
async def check_coldkey_swap(wallet: Wallet, subtensor: SubtensorInterface):
|
1694
|
-
arbitration_check = len(
|
1382
|
+
arbitration_check = len( # TODO verify this works
|
1695
1383
|
(
|
1696
|
-
await subtensor.
|
1384
|
+
await subtensor.query(
|
1697
1385
|
module="SubtensorModule",
|
1698
1386
|
storage_function="ColdkeySwapDestinations",
|
1699
1387
|
params=[wallet.coldkeypub.ss58_address],
|
1700
1388
|
)
|
1701
|
-
)
|
1389
|
+
)
|
1702
1390
|
)
|
1703
1391
|
if arbitration_check == 0:
|
1704
1392
|
console.print(
|
1705
1393
|
"[green]There has been no previous key swap initiated for your coldkey.[/green]"
|
1706
1394
|
)
|
1707
1395
|
elif arbitration_check == 1:
|
1708
|
-
arbitration_block = await subtensor.
|
1396
|
+
arbitration_block = await subtensor.query(
|
1709
1397
|
module="SubtensorModule",
|
1710
1398
|
storage_function="ColdkeyArbitrationBlock",
|
1711
1399
|
params=[wallet.coldkeypub.ss58_address],
|
@@ -1729,17 +1417,25 @@ async def check_coldkey_swap(wallet: Wallet, subtensor: SubtensorInterface):
|
|
1729
1417
|
|
1730
1418
|
async def sign(wallet: Wallet, message: str, use_hotkey: str):
|
1731
1419
|
"""Sign a message using the provided wallet or hotkey."""
|
1420
|
+
|
1421
|
+
try:
|
1422
|
+
wallet.unlock_coldkey()
|
1423
|
+
except KeyFileError:
|
1424
|
+
err_console.print(
|
1425
|
+
":cross_mark: [red]Keyfile is corrupt, non-writable, non-readable or the password used to decrypt is "
|
1426
|
+
"invalid[/red]:[bold white]\n [/bold white]"
|
1427
|
+
)
|
1732
1428
|
if not use_hotkey:
|
1733
|
-
if not unlock_key(wallet).success:
|
1734
|
-
return False
|
1735
1429
|
keypair = wallet.coldkey
|
1736
|
-
print_verbose(
|
1430
|
+
print_verbose(
|
1431
|
+
f"Signing using [{COLOR_PALETTE['GENERAL']['COLDKEY']}]coldkey: {wallet.name}"
|
1432
|
+
)
|
1737
1433
|
else:
|
1738
|
-
if not unlock_key(wallet, "hot").success:
|
1739
|
-
return False
|
1740
1434
|
keypair = wallet.hotkey
|
1741
|
-
print_verbose(
|
1435
|
+
print_verbose(
|
1436
|
+
f"Signing using [{COLOR_PALETTE['GENERAL']['HOTKEY']}]hotkey: {wallet.hotkey_str}"
|
1437
|
+
)
|
1742
1438
|
|
1743
1439
|
signed_message = keypair.sign(message.encode("utf-8")).hex()
|
1744
|
-
console.print("[
|
1440
|
+
console.print("[dark_sea_green3]Message signed successfully:")
|
1745
1441
|
console.print(signed_message)
|