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