bittensor-cli 8.4.4__py3-none-any.whl → 9.0.0__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 +1 -1
- bittensor_cli/cli.py +1827 -1394
- bittensor_cli/src/__init__.py +623 -168
- bittensor_cli/src/bittensor/balances.py +41 -8
- bittensor_cli/src/bittensor/chain_data.py +557 -428
- bittensor_cli/src/bittensor/extrinsics/registration.py +129 -23
- bittensor_cli/src/bittensor/extrinsics/root.py +3 -3
- bittensor_cli/src/bittensor/extrinsics/transfer.py +6 -11
- bittensor_cli/src/bittensor/minigraph.py +46 -8
- bittensor_cli/src/bittensor/subtensor_interface.py +567 -250
- bittensor_cli/src/bittensor/utils.py +370 -25
- bittensor_cli/src/commands/stake/__init__.py +154 -0
- bittensor_cli/src/commands/stake/add.py +625 -0
- bittensor_cli/src/commands/stake/children_hotkeys.py +103 -75
- bittensor_cli/src/commands/stake/list.py +687 -0
- bittensor_cli/src/commands/stake/move.py +1000 -0
- bittensor_cli/src/commands/stake/remove.py +1146 -0
- 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 +2028 -0
- bittensor_cli/src/commands/sudo.py +554 -12
- bittensor_cli/src/commands/wallets.py +225 -531
- bittensor_cli/src/commands/weights.py +2 -2
- {bittensor_cli-8.4.4.dist-info → bittensor_cli-9.0.0.dist-info}/METADATA +7 -4
- bittensor_cli-9.0.0.dist-info/RECORD +34 -0
- bittensor_cli/src/bittensor/async_substrate_interface.py +0 -2748
- bittensor_cli/src/commands/root.py +0 -1787
- bittensor_cli/src/commands/stake/stake.py +0 -1448
- bittensor_cli/src/commands/subnets.py +0 -897
- bittensor_cli-8.4.4.dist-info/RECORD +0 -31
- {bittensor_cli-8.4.4.dist-info → bittensor_cli-9.0.0.dist-info}/WHEEL +0 -0
- {bittensor_cli-8.4.4.dist-info → bittensor_cli-9.0.0.dist-info}/entry_points.txt +0 -0
- {bittensor_cli-8.4.4.dist-info → bittensor_cli-9.0.0.dist-info}/top_level.txt +0 -0
@@ -1,897 +0,0 @@
|
|
1
|
-
import asyncio
|
2
|
-
import json
|
3
|
-
import sqlite3
|
4
|
-
from textwrap import dedent
|
5
|
-
from typing import TYPE_CHECKING, Optional, cast
|
6
|
-
|
7
|
-
from bittensor_wallet import Wallet
|
8
|
-
from rich.prompt import Confirm
|
9
|
-
from rich.table import Column, Table
|
10
|
-
|
11
|
-
from bittensor_cli.src import DelegatesDetails
|
12
|
-
from bittensor_cli.src.bittensor.balances import Balance
|
13
|
-
from bittensor_cli.src.bittensor.chain_data import SubnetInfo
|
14
|
-
from bittensor_cli.src.bittensor.extrinsics.registration import register_extrinsic
|
15
|
-
from bittensor_cli.src.bittensor.minigraph import MiniGraph
|
16
|
-
from bittensor_cli.src.commands.root import burned_register_extrinsic
|
17
|
-
from bittensor_cli.src.commands.wallets import set_id, set_id_prompts
|
18
|
-
from bittensor_cli.src.bittensor.utils import (
|
19
|
-
RAO_PER_TAO,
|
20
|
-
console,
|
21
|
-
create_table,
|
22
|
-
err_console,
|
23
|
-
print_verbose,
|
24
|
-
print_error,
|
25
|
-
format_error_message,
|
26
|
-
get_metadata_table,
|
27
|
-
millify,
|
28
|
-
render_table,
|
29
|
-
update_metadata_table,
|
30
|
-
unlock_key,
|
31
|
-
hex_to_bytes,
|
32
|
-
)
|
33
|
-
|
34
|
-
if TYPE_CHECKING:
|
35
|
-
from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface
|
36
|
-
|
37
|
-
|
38
|
-
# helpers and extrinsics
|
39
|
-
|
40
|
-
|
41
|
-
async def register_subnetwork_extrinsic(
|
42
|
-
subtensor: "SubtensorInterface",
|
43
|
-
wallet: Wallet,
|
44
|
-
wait_for_inclusion: bool = False,
|
45
|
-
wait_for_finalization: bool = True,
|
46
|
-
prompt: bool = False,
|
47
|
-
) -> bool:
|
48
|
-
"""Registers a new subnetwork.
|
49
|
-
|
50
|
-
wallet (bittensor.wallet):
|
51
|
-
bittensor wallet object.
|
52
|
-
wait_for_inclusion (bool):
|
53
|
-
If set, waits for the extrinsic to enter a block before returning ``true``, or returns ``false`` if the extrinsic fails to enter the block within the timeout.
|
54
|
-
wait_for_finalization (bool):
|
55
|
-
If set, waits for the extrinsic to be finalized on the chain before returning ``true``, or returns ``false`` if the extrinsic fails to be finalized within the timeout.
|
56
|
-
prompt (bool):
|
57
|
-
If true, the call waits for confirmation from the user before proceeding.
|
58
|
-
Returns:
|
59
|
-
success (bool):
|
60
|
-
Flag is ``true`` if extrinsic was finalized or included in the block.
|
61
|
-
If we did not wait for finalization / inclusion, the response is ``true``.
|
62
|
-
"""
|
63
|
-
|
64
|
-
async def _find_event_attributes_in_extrinsic_receipt(
|
65
|
-
response_, event_name: str
|
66
|
-
) -> list:
|
67
|
-
"""
|
68
|
-
Searches for the attributes of a specified event within an extrinsic receipt.
|
69
|
-
|
70
|
-
:param response_: (substrateinterface.base.ExtrinsicReceipt): The receipt of the extrinsic to be searched.
|
71
|
-
:param event_name: The name of the event to search for.
|
72
|
-
|
73
|
-
:return: A list of attributes for the specified event. Returns [-1] if the event is not found.
|
74
|
-
"""
|
75
|
-
for event in await response_.triggered_events:
|
76
|
-
# Access the event details
|
77
|
-
event_details = event["event"]
|
78
|
-
# Check if the event_id is 'NetworkAdded'
|
79
|
-
if event_details["event_id"] == event_name:
|
80
|
-
# Once found, you can access the attributes of the event_name
|
81
|
-
return event_details["attributes"]
|
82
|
-
return [-1]
|
83
|
-
|
84
|
-
print_verbose("Fetching balance")
|
85
|
-
your_balance_ = await subtensor.get_balance(wallet.coldkeypub.ss58_address)
|
86
|
-
your_balance = your_balance_[wallet.coldkeypub.ss58_address]
|
87
|
-
|
88
|
-
print_verbose("Fetching lock_cost")
|
89
|
-
burn_cost = await lock_cost(subtensor)
|
90
|
-
if burn_cost > your_balance:
|
91
|
-
err_console.print(
|
92
|
-
f"Your balance of: [green]{your_balance}[/green] is not enough to pay the subnet lock cost of: "
|
93
|
-
f"[green]{burn_cost}[/green]"
|
94
|
-
)
|
95
|
-
return False
|
96
|
-
|
97
|
-
if prompt:
|
98
|
-
console.print(f"Your balance is: [green]{your_balance}[/green]")
|
99
|
-
if not Confirm.ask(
|
100
|
-
f"Do you want to register a subnet for [green]{burn_cost}[/green]?"
|
101
|
-
):
|
102
|
-
return False
|
103
|
-
|
104
|
-
if not unlock_key(wallet).success:
|
105
|
-
return False
|
106
|
-
|
107
|
-
with console.status(":satellite: Registering subnet...", spinner="earth"):
|
108
|
-
substrate = subtensor.substrate
|
109
|
-
# create extrinsic call
|
110
|
-
call = await substrate.compose_call(
|
111
|
-
call_module="SubtensorModule",
|
112
|
-
call_function="register_network",
|
113
|
-
call_params={"immunity_period": 0, "reg_allowed": True},
|
114
|
-
)
|
115
|
-
extrinsic = await substrate.create_signed_extrinsic(
|
116
|
-
call=call, keypair=wallet.coldkey
|
117
|
-
)
|
118
|
-
response = await substrate.submit_extrinsic(
|
119
|
-
extrinsic,
|
120
|
-
wait_for_inclusion=wait_for_inclusion,
|
121
|
-
wait_for_finalization=wait_for_finalization,
|
122
|
-
)
|
123
|
-
|
124
|
-
# We only wait here if we expect finalization.
|
125
|
-
if not wait_for_finalization and not wait_for_inclusion:
|
126
|
-
return True
|
127
|
-
|
128
|
-
await response.process_events()
|
129
|
-
if not await response.is_success:
|
130
|
-
err_console.print(
|
131
|
-
f":cross_mark: [red]Failed[/red]: {format_error_message(await response.error_message)}"
|
132
|
-
)
|
133
|
-
await asyncio.sleep(0.5)
|
134
|
-
return False
|
135
|
-
|
136
|
-
# Successful registration, final check for membership
|
137
|
-
else:
|
138
|
-
attributes = await _find_event_attributes_in_extrinsic_receipt(
|
139
|
-
response, "NetworkAdded"
|
140
|
-
)
|
141
|
-
console.print(
|
142
|
-
f":white_heavy_check_mark: [green]Registered subnetwork with netuid: {attributes[0]}[/green]"
|
143
|
-
)
|
144
|
-
return True
|
145
|
-
|
146
|
-
|
147
|
-
# commands
|
148
|
-
|
149
|
-
|
150
|
-
async def subnets_list(
|
151
|
-
subtensor: "SubtensorInterface", reuse_last: bool, html_output: bool, no_cache: bool
|
152
|
-
):
|
153
|
-
"""List all subnet netuids in the network."""
|
154
|
-
|
155
|
-
async def _get_all_subnets_info():
|
156
|
-
hex_bytes_result = await subtensor.query_runtime_api(
|
157
|
-
runtime_api="SubnetInfoRuntimeApi", method="get_subnets_info", params=[]
|
158
|
-
)
|
159
|
-
|
160
|
-
return SubnetInfo.list_from_vec_u8(hex_to_bytes(hex_bytes_result))
|
161
|
-
|
162
|
-
if not reuse_last:
|
163
|
-
subnets: list[SubnetInfo]
|
164
|
-
delegate_info: dict[str, DelegatesDetails]
|
165
|
-
|
166
|
-
print_verbose("Fetching subnet and delegate information")
|
167
|
-
subnets, delegate_info = await asyncio.gather(
|
168
|
-
_get_all_subnets_info(),
|
169
|
-
subtensor.get_delegate_identities(),
|
170
|
-
)
|
171
|
-
|
172
|
-
if not subnets:
|
173
|
-
err_console.print("[red]No subnets found[/red]")
|
174
|
-
return
|
175
|
-
|
176
|
-
rows = []
|
177
|
-
db_rows = []
|
178
|
-
total_neurons = 0
|
179
|
-
max_neurons = 0
|
180
|
-
|
181
|
-
for subnet in subnets:
|
182
|
-
total_neurons += subnet.subnetwork_n
|
183
|
-
max_neurons += subnet.max_n
|
184
|
-
rows.append(
|
185
|
-
(
|
186
|
-
str(subnet.netuid),
|
187
|
-
str(subnet.subnetwork_n),
|
188
|
-
str(millify(subnet.max_n)),
|
189
|
-
f"{subnet.emission_value / RAO_PER_TAO * 100:0.2f}%",
|
190
|
-
str(subnet.tempo),
|
191
|
-
f"{subnet.burn!s:8.8}",
|
192
|
-
str(millify(subnet.difficulty)),
|
193
|
-
str(
|
194
|
-
delegate_info[subnet.owner_ss58].display
|
195
|
-
if subnet.owner_ss58 in delegate_info
|
196
|
-
else subnet.owner_ss58
|
197
|
-
),
|
198
|
-
)
|
199
|
-
)
|
200
|
-
db_rows.append(
|
201
|
-
[
|
202
|
-
int(subnet.netuid),
|
203
|
-
int(subnet.subnetwork_n),
|
204
|
-
int(subnet.max_n), # millified in HTML table
|
205
|
-
float(
|
206
|
-
subnet.emission_value / RAO_PER_TAO * 100
|
207
|
-
), # shown as percentage in HTML table
|
208
|
-
int(subnet.tempo),
|
209
|
-
float(subnet.burn),
|
210
|
-
int(subnet.difficulty), # millified in HTML table
|
211
|
-
str(
|
212
|
-
delegate_info[subnet.owner_ss58].display
|
213
|
-
if subnet.owner_ss58 in delegate_info
|
214
|
-
else subnet.owner_ss58
|
215
|
-
),
|
216
|
-
]
|
217
|
-
)
|
218
|
-
metadata = {
|
219
|
-
"network": subtensor.network,
|
220
|
-
"netuid_count": len(subnets),
|
221
|
-
"N": total_neurons,
|
222
|
-
"MAX_N": max_neurons,
|
223
|
-
"rows": json.dumps(rows),
|
224
|
-
}
|
225
|
-
if not no_cache:
|
226
|
-
create_table(
|
227
|
-
"subnetslist",
|
228
|
-
[
|
229
|
-
("NETUID", "INTEGER"),
|
230
|
-
("N", "INTEGER"),
|
231
|
-
("MAX_N", "BLOB"),
|
232
|
-
("EMISSION", "REAL"),
|
233
|
-
("TEMPO", "INTEGER"),
|
234
|
-
("RECYCLE", "REAL"),
|
235
|
-
("DIFFICULTY", "BLOB"),
|
236
|
-
("SUDO", "TEXT"),
|
237
|
-
],
|
238
|
-
db_rows,
|
239
|
-
)
|
240
|
-
update_metadata_table("subnetslist", values=metadata)
|
241
|
-
else:
|
242
|
-
try:
|
243
|
-
metadata = get_metadata_table("subnetslist")
|
244
|
-
rows = json.loads(metadata["rows"])
|
245
|
-
except sqlite3.OperationalError:
|
246
|
-
err_console.print(
|
247
|
-
"[red]Error[/red] Unable to retrieve table data. This is usually caused by attempting to use "
|
248
|
-
"`--reuse-last` before running the command a first time. In rare cases, this could also be due to "
|
249
|
-
"a corrupted database. Re-run the command (do not use `--reuse-last`) and see if that resolves your "
|
250
|
-
"issue."
|
251
|
-
)
|
252
|
-
return
|
253
|
-
if not html_output:
|
254
|
-
table = Table(
|
255
|
-
title=f"[underline dark_orange]Subnets[/underline dark_orange]\n[dark_orange]Network: {metadata['network']}[/dark_orange]\n",
|
256
|
-
show_footer=True,
|
257
|
-
show_edge=False,
|
258
|
-
header_style="bold white",
|
259
|
-
border_style="bright_black",
|
260
|
-
style="bold",
|
261
|
-
title_justify="center",
|
262
|
-
show_lines=False,
|
263
|
-
pad_edge=True,
|
264
|
-
)
|
265
|
-
|
266
|
-
table.add_column(
|
267
|
-
"[bold white]NETUID",
|
268
|
-
footer=f"[white]{metadata['netuid_count']}[/white]",
|
269
|
-
style="white",
|
270
|
-
justify="center",
|
271
|
-
)
|
272
|
-
table.add_column(
|
273
|
-
"[bold white]N",
|
274
|
-
footer=f"[white]{metadata['N']}[/white]",
|
275
|
-
style="bright_cyan",
|
276
|
-
justify="right",
|
277
|
-
)
|
278
|
-
table.add_column(
|
279
|
-
"[bold white]MAX_N",
|
280
|
-
footer=f"[white]{metadata['MAX_N']}[/white]",
|
281
|
-
style="bright_cyan",
|
282
|
-
justify="right",
|
283
|
-
)
|
284
|
-
table.add_column(
|
285
|
-
"[bold white]EMISSION", style="light_goldenrod2", justify="right"
|
286
|
-
)
|
287
|
-
table.add_column("[bold white]TEMPO", style="rgb(42,161,152)", justify="right")
|
288
|
-
table.add_column("[bold white]RECYCLE", style="light_salmon3", justify="right")
|
289
|
-
table.add_column("[bold white]POW", style="medium_purple", justify="right")
|
290
|
-
table.add_column(
|
291
|
-
"[bold white]SUDO", style="bright_magenta", justify="right", overflow="fold"
|
292
|
-
)
|
293
|
-
|
294
|
-
for row in rows:
|
295
|
-
table.add_row(*row)
|
296
|
-
|
297
|
-
console.print(table)
|
298
|
-
console.print(
|
299
|
-
dedent(
|
300
|
-
"""
|
301
|
-
Description:
|
302
|
-
The table displays the list of subnets registered in the Bittensor network.
|
303
|
-
- NETUID: The network identifier of the subnet.
|
304
|
-
- N: The current UIDs registered to the network.
|
305
|
-
- MAX_N: The total UIDs allowed on the network.
|
306
|
-
- EMISSION: The emission accrued by this subnet in the network.
|
307
|
-
- TEMPO: A duration of a number of blocks. Several subnet events occur at the end of every tempo period.
|
308
|
-
- RECYCLE: Cost to register to the subnet.
|
309
|
-
- POW: Proof of work metric of the subnet.
|
310
|
-
- SUDO: Owner's identity.
|
311
|
-
"""
|
312
|
-
)
|
313
|
-
)
|
314
|
-
else:
|
315
|
-
render_table(
|
316
|
-
"subnetslist",
|
317
|
-
f"Subnets List | Network: {metadata['network']} - "
|
318
|
-
f"Netuids: {metadata['netuid_count']} - N: {metadata['N']}",
|
319
|
-
columns=[
|
320
|
-
{"title": "NetUID", "field": "NETUID"},
|
321
|
-
{"title": "N", "field": "N"},
|
322
|
-
{"title": "MAX_N", "field": "MAX_N", "customFormatter": "millify"},
|
323
|
-
{
|
324
|
-
"title": "EMISSION",
|
325
|
-
"field": "EMISSION",
|
326
|
-
"formatter": "money",
|
327
|
-
"formatterParams": {
|
328
|
-
"symbolAfter": "p",
|
329
|
-
"symbol": "%",
|
330
|
-
"precision": 2,
|
331
|
-
},
|
332
|
-
},
|
333
|
-
{"title": "Tempo", "field": "TEMPO"},
|
334
|
-
{
|
335
|
-
"title": "Recycle",
|
336
|
-
"field": "RECYCLE",
|
337
|
-
"formatter": "money",
|
338
|
-
"formatterParams": {"symbol": "τ", "precision": 5},
|
339
|
-
},
|
340
|
-
{
|
341
|
-
"title": "Difficulty",
|
342
|
-
"field": "DIFFICULTY",
|
343
|
-
"customFormatter": "millify",
|
344
|
-
},
|
345
|
-
{"title": "sudo", "field": "SUDO"},
|
346
|
-
],
|
347
|
-
)
|
348
|
-
|
349
|
-
|
350
|
-
async def lock_cost(subtensor: "SubtensorInterface") -> Optional[Balance]:
|
351
|
-
"""View locking cost of creating a new subnetwork"""
|
352
|
-
with console.status(
|
353
|
-
f":satellite:Retrieving lock cost from {subtensor.network}...",
|
354
|
-
spinner="aesthetic",
|
355
|
-
):
|
356
|
-
lc = await subtensor.query_runtime_api(
|
357
|
-
runtime_api="SubnetRegistrationRuntimeApi",
|
358
|
-
method="get_network_registration_cost",
|
359
|
-
params=[],
|
360
|
-
)
|
361
|
-
if lc:
|
362
|
-
lock_cost_ = Balance(lc)
|
363
|
-
console.print(f"Subnet lock cost: [green]{lock_cost_}[/green]")
|
364
|
-
return lock_cost_
|
365
|
-
else:
|
366
|
-
err_console.print("Subnet lock cost: [red]Failed to get subnet lock cost[/red]")
|
367
|
-
return None
|
368
|
-
|
369
|
-
|
370
|
-
async def create(wallet: Wallet, subtensor: "SubtensorInterface", prompt: bool):
|
371
|
-
"""Register a subnetwork"""
|
372
|
-
|
373
|
-
# Call register command.
|
374
|
-
success = await register_subnetwork_extrinsic(subtensor, wallet, prompt=prompt)
|
375
|
-
if success and prompt:
|
376
|
-
# Prompt for user to set identity.
|
377
|
-
do_set_identity = Confirm.ask(
|
378
|
-
"Subnetwork registered successfully. Would you like to set your identity?"
|
379
|
-
)
|
380
|
-
|
381
|
-
if do_set_identity:
|
382
|
-
id_prompts = set_id_prompts(validator=False)
|
383
|
-
await set_id(wallet, subtensor, *id_prompts, prompt=prompt)
|
384
|
-
|
385
|
-
|
386
|
-
async def pow_register(
|
387
|
-
wallet: Wallet,
|
388
|
-
subtensor: "SubtensorInterface",
|
389
|
-
netuid,
|
390
|
-
processors,
|
391
|
-
update_interval,
|
392
|
-
output_in_place,
|
393
|
-
verbose,
|
394
|
-
use_cuda,
|
395
|
-
dev_id,
|
396
|
-
threads_per_block,
|
397
|
-
prompt: bool,
|
398
|
-
):
|
399
|
-
"""Register neuron."""
|
400
|
-
|
401
|
-
await register_extrinsic(
|
402
|
-
subtensor,
|
403
|
-
wallet=wallet,
|
404
|
-
netuid=netuid,
|
405
|
-
prompt=prompt,
|
406
|
-
tpb=threads_per_block,
|
407
|
-
update_interval=update_interval,
|
408
|
-
num_processes=processors,
|
409
|
-
cuda=use_cuda,
|
410
|
-
dev_id=dev_id,
|
411
|
-
output_in_place=output_in_place,
|
412
|
-
log_verbose=verbose,
|
413
|
-
)
|
414
|
-
|
415
|
-
|
416
|
-
async def register(
|
417
|
-
wallet: Wallet, subtensor: "SubtensorInterface", netuid: int, prompt: bool
|
418
|
-
):
|
419
|
-
"""Register neuron by recycling some TAO."""
|
420
|
-
|
421
|
-
# Verify subnet exists
|
422
|
-
print_verbose("Checking subnet status")
|
423
|
-
block_hash = await subtensor.substrate.get_chain_head()
|
424
|
-
if not await subtensor.subnet_exists(netuid=netuid, block_hash=block_hash):
|
425
|
-
err_console.print(f"[red]Subnet {netuid} does not exist[/red]")
|
426
|
-
return
|
427
|
-
|
428
|
-
# Check current recycle amount
|
429
|
-
print_verbose("Fetching recycle amount")
|
430
|
-
current_recycle_, balance_ = await asyncio.gather(
|
431
|
-
subtensor.get_hyperparameter(
|
432
|
-
param_name="Burn", netuid=netuid, block_hash=block_hash
|
433
|
-
),
|
434
|
-
subtensor.get_balance(wallet.coldkeypub.ss58_address, block_hash=block_hash),
|
435
|
-
)
|
436
|
-
current_recycle = (
|
437
|
-
Balance.from_rao(int(current_recycle_)) if current_recycle_ else Balance(0)
|
438
|
-
)
|
439
|
-
balance = balance_[wallet.coldkeypub.ss58_address]
|
440
|
-
|
441
|
-
# Check balance is sufficient
|
442
|
-
if balance < current_recycle:
|
443
|
-
err_console.print(
|
444
|
-
f"[red]Insufficient balance {balance} to register neuron. Current recycle is {current_recycle} TAO[/red]"
|
445
|
-
)
|
446
|
-
return
|
447
|
-
|
448
|
-
if prompt:
|
449
|
-
if not (
|
450
|
-
Confirm.ask(
|
451
|
-
f"Your balance is: [bold green]{balance}[/bold green]\nThe cost to register by recycle is "
|
452
|
-
f"[bold red]{current_recycle}[/bold red]\nDo you want to continue?",
|
453
|
-
default=False,
|
454
|
-
)
|
455
|
-
):
|
456
|
-
return
|
457
|
-
|
458
|
-
await burned_register_extrinsic(
|
459
|
-
subtensor,
|
460
|
-
wallet=wallet,
|
461
|
-
netuid=netuid,
|
462
|
-
prompt=False,
|
463
|
-
recycle_amount=current_recycle,
|
464
|
-
old_balance=balance,
|
465
|
-
)
|
466
|
-
|
467
|
-
|
468
|
-
async def metagraph_cmd(
|
469
|
-
subtensor: Optional["SubtensorInterface"],
|
470
|
-
netuid: Optional[int],
|
471
|
-
reuse_last: bool,
|
472
|
-
html_output: bool,
|
473
|
-
no_cache: bool,
|
474
|
-
display_cols: dict,
|
475
|
-
):
|
476
|
-
"""Prints an entire metagraph."""
|
477
|
-
# TODO allow config to set certain columns
|
478
|
-
if not reuse_last:
|
479
|
-
cast("SubtensorInterface", subtensor)
|
480
|
-
cast(int, netuid)
|
481
|
-
with console.status(
|
482
|
-
f":satellite: Syncing with chain: [white]{subtensor.network}[/white] ...",
|
483
|
-
spinner="aesthetic",
|
484
|
-
) as status:
|
485
|
-
block_hash = await subtensor.substrate.get_chain_head()
|
486
|
-
|
487
|
-
if not await subtensor.subnet_exists(netuid, block_hash):
|
488
|
-
print_error(f"Subnet with netuid: {netuid} does not exist", status)
|
489
|
-
return False
|
490
|
-
|
491
|
-
neurons, difficulty_, total_issuance_, block = await asyncio.gather(
|
492
|
-
subtensor.neurons(netuid, block_hash=block_hash),
|
493
|
-
subtensor.get_hyperparameter(
|
494
|
-
param_name="Difficulty", netuid=netuid, block_hash=block_hash
|
495
|
-
),
|
496
|
-
subtensor.substrate.query(
|
497
|
-
module="SubtensorModule",
|
498
|
-
storage_function="TotalIssuance",
|
499
|
-
params=[],
|
500
|
-
block_hash=block_hash,
|
501
|
-
),
|
502
|
-
subtensor.substrate.get_block_number(block_hash=block_hash),
|
503
|
-
)
|
504
|
-
|
505
|
-
difficulty = int(difficulty_)
|
506
|
-
total_issuance = Balance.from_rao(total_issuance_)
|
507
|
-
metagraph = MiniGraph(
|
508
|
-
netuid=netuid, neurons=neurons, subtensor=subtensor, block=block
|
509
|
-
)
|
510
|
-
table_data = []
|
511
|
-
db_table = []
|
512
|
-
total_stake = 0.0
|
513
|
-
total_rank = 0.0
|
514
|
-
total_validator_trust = 0.0
|
515
|
-
total_trust = 0.0
|
516
|
-
total_consensus = 0.0
|
517
|
-
total_incentive = 0.0
|
518
|
-
total_dividends = 0.0
|
519
|
-
total_emission = 0
|
520
|
-
for uid in metagraph.uids:
|
521
|
-
neuron = metagraph.neurons[uid]
|
522
|
-
ep = metagraph.axons[uid]
|
523
|
-
row = [
|
524
|
-
str(neuron.uid),
|
525
|
-
"{:.5f}".format(metagraph.total_stake[uid]),
|
526
|
-
"{:.5f}".format(metagraph.ranks[uid]),
|
527
|
-
"{:.5f}".format(metagraph.trust[uid]),
|
528
|
-
"{:.5f}".format(metagraph.consensus[uid]),
|
529
|
-
"{:.5f}".format(metagraph.incentive[uid]),
|
530
|
-
"{:.5f}".format(metagraph.dividends[uid]),
|
531
|
-
"{}".format(int(metagraph.emission[uid] * 1000000000)),
|
532
|
-
"{:.5f}".format(metagraph.validator_trust[uid]),
|
533
|
-
"*" if metagraph.validator_permit[uid] else "",
|
534
|
-
str(metagraph.block.item() - metagraph.last_update[uid].item()),
|
535
|
-
str(metagraph.active[uid].item()),
|
536
|
-
(
|
537
|
-
ep.ip + ":" + str(ep.port)
|
538
|
-
if ep.is_serving
|
539
|
-
else "[light_goldenrod2]none[/light_goldenrod2]"
|
540
|
-
),
|
541
|
-
ep.hotkey[:10],
|
542
|
-
ep.coldkey[:10],
|
543
|
-
]
|
544
|
-
db_row = [
|
545
|
-
neuron.uid,
|
546
|
-
float(metagraph.total_stake[uid]),
|
547
|
-
float(metagraph.ranks[uid]),
|
548
|
-
float(metagraph.trust[uid]),
|
549
|
-
float(metagraph.consensus[uid]),
|
550
|
-
float(metagraph.incentive[uid]),
|
551
|
-
float(metagraph.dividends[uid]),
|
552
|
-
int(metagraph.emission[uid] * 1000000000),
|
553
|
-
float(metagraph.validator_trust[uid]),
|
554
|
-
bool(metagraph.validator_permit[uid]),
|
555
|
-
metagraph.block.item() - metagraph.last_update[uid].item(),
|
556
|
-
metagraph.active[uid].item(),
|
557
|
-
(ep.ip + ":" + str(ep.port) if ep.is_serving else "ERROR"),
|
558
|
-
ep.hotkey[:10],
|
559
|
-
ep.coldkey[:10],
|
560
|
-
]
|
561
|
-
db_table.append(db_row)
|
562
|
-
total_stake += metagraph.total_stake[uid]
|
563
|
-
total_rank += metagraph.ranks[uid]
|
564
|
-
total_validator_trust += metagraph.validator_trust[uid]
|
565
|
-
total_trust += metagraph.trust[uid]
|
566
|
-
total_consensus += metagraph.consensus[uid]
|
567
|
-
total_incentive += metagraph.incentive[uid]
|
568
|
-
total_dividends += metagraph.dividends[uid]
|
569
|
-
total_emission += int(metagraph.emission[uid] * 1000000000)
|
570
|
-
table_data.append(row)
|
571
|
-
metadata_info = {
|
572
|
-
"stake": str(Balance.from_tao(total_stake)),
|
573
|
-
"total_stake": "\u03c4{:.5f}".format(total_stake),
|
574
|
-
"rank": "{:.5f}".format(total_rank),
|
575
|
-
"validator_trust": "{:.5f}".format(total_validator_trust),
|
576
|
-
"trust": "{:.5f}".format(total_trust),
|
577
|
-
"consensus": "{:.5f}".format(total_consensus),
|
578
|
-
"incentive": "{:.5f}".format(total_incentive),
|
579
|
-
"dividends": "{:.5f}".format(total_dividends),
|
580
|
-
"emission": "\u03c1{}".format(int(total_emission)),
|
581
|
-
"net": f"{subtensor.network}:{metagraph.netuid}",
|
582
|
-
"block": str(metagraph.block.item()),
|
583
|
-
"N": f"{sum(metagraph.active.tolist())}/{metagraph.n.item()}",
|
584
|
-
"N0": str(sum(metagraph.active.tolist())),
|
585
|
-
"N1": str(metagraph.n.item()),
|
586
|
-
"issuance": str(total_issuance),
|
587
|
-
"difficulty": str(difficulty),
|
588
|
-
"total_neurons": str(len(metagraph.uids)),
|
589
|
-
"table_data": json.dumps(table_data),
|
590
|
-
}
|
591
|
-
if not no_cache:
|
592
|
-
update_metadata_table("metagraph", metadata_info)
|
593
|
-
create_table(
|
594
|
-
"metagraph",
|
595
|
-
columns=[
|
596
|
-
("UID", "INTEGER"),
|
597
|
-
("STAKE", "REAL"),
|
598
|
-
("RANK", "REAL"),
|
599
|
-
("TRUST", "REAL"),
|
600
|
-
("CONSENSUS", "REAL"),
|
601
|
-
("INCENTIVE", "REAL"),
|
602
|
-
("DIVIDENDS", "REAL"),
|
603
|
-
("EMISSION", "INTEGER"),
|
604
|
-
("VTRUST", "REAL"),
|
605
|
-
("VAL", "INTEGER"),
|
606
|
-
("UPDATED", "INTEGER"),
|
607
|
-
("ACTIVE", "INTEGER"),
|
608
|
-
("AXON", "TEXT"),
|
609
|
-
("HOTKEY", "TEXT"),
|
610
|
-
("COLDKEY", "TEXT"),
|
611
|
-
],
|
612
|
-
rows=db_table,
|
613
|
-
)
|
614
|
-
else:
|
615
|
-
try:
|
616
|
-
metadata_info = get_metadata_table("metagraph")
|
617
|
-
table_data = json.loads(metadata_info["table_data"])
|
618
|
-
except sqlite3.OperationalError:
|
619
|
-
err_console.print(
|
620
|
-
"[red]Error[/red] Unable to retrieve table data. This is usually caused by attempting to use "
|
621
|
-
"`--reuse-last` before running the command a first time. In rare cases, this could also be due to "
|
622
|
-
"a corrupted database. Re-run the command (do not use `--reuse-last`) and see if that resolves your "
|
623
|
-
"issue."
|
624
|
-
)
|
625
|
-
return
|
626
|
-
|
627
|
-
if html_output:
|
628
|
-
try:
|
629
|
-
render_table(
|
630
|
-
table_name="metagraph",
|
631
|
-
table_info=f"Metagraph | "
|
632
|
-
f"net: {metadata_info['net']}, "
|
633
|
-
f"block: {metadata_info['block']}, "
|
634
|
-
f"N: {metadata_info['N']}, "
|
635
|
-
f"stake: {metadata_info['stake']}, "
|
636
|
-
f"issuance: {metadata_info['issuance']}, "
|
637
|
-
f"difficulty: {metadata_info['difficulty']}",
|
638
|
-
columns=[
|
639
|
-
{"title": "UID", "field": "UID"},
|
640
|
-
{
|
641
|
-
"title": "Stake",
|
642
|
-
"field": "STAKE",
|
643
|
-
"formatter": "money",
|
644
|
-
"formatterParams": {"symbol": "τ", "precision": 5},
|
645
|
-
},
|
646
|
-
{
|
647
|
-
"title": "Rank",
|
648
|
-
"field": "RANK",
|
649
|
-
"formatter": "money",
|
650
|
-
"formatterParams": {"precision": 5},
|
651
|
-
},
|
652
|
-
{
|
653
|
-
"title": "Trust",
|
654
|
-
"field": "TRUST",
|
655
|
-
"formatter": "money",
|
656
|
-
"formatterParams": {"precision": 5},
|
657
|
-
},
|
658
|
-
{
|
659
|
-
"title": "Consensus",
|
660
|
-
"field": "CONSENSUS",
|
661
|
-
"formatter": "money",
|
662
|
-
"formatterParams": {"precision": 5},
|
663
|
-
},
|
664
|
-
{
|
665
|
-
"title": "Incentive",
|
666
|
-
"field": "INCENTIVE",
|
667
|
-
"formatter": "money",
|
668
|
-
"formatterParams": {"precision": 5},
|
669
|
-
},
|
670
|
-
{
|
671
|
-
"title": "Dividends",
|
672
|
-
"field": "DIVIDENDS",
|
673
|
-
"formatter": "money",
|
674
|
-
"formatterParams": {"precision": 5},
|
675
|
-
},
|
676
|
-
{"title": "Emission", "field": "EMISSION"},
|
677
|
-
{
|
678
|
-
"title": "VTrust",
|
679
|
-
"field": "VTRUST",
|
680
|
-
"formatter": "money",
|
681
|
-
"formatterParams": {"precision": 5},
|
682
|
-
},
|
683
|
-
{"title": "Validated", "field": "VAL"},
|
684
|
-
{"title": "Updated", "field": "UPDATED"},
|
685
|
-
{"title": "Active", "field": "ACTIVE"},
|
686
|
-
{"title": "Axon", "field": "AXON"},
|
687
|
-
{"title": "Hotkey", "field": "HOTKEY"},
|
688
|
-
{"title": "Coldkey", "field": "COLDKEY"},
|
689
|
-
],
|
690
|
-
)
|
691
|
-
except sqlite3.OperationalError:
|
692
|
-
err_console.print(
|
693
|
-
"[red]Error[/red] Unable to retrieve table data. This may indicate that your database is corrupted, "
|
694
|
-
"or was not able to load with the most recent data."
|
695
|
-
)
|
696
|
-
return
|
697
|
-
else:
|
698
|
-
cols: dict[str, tuple[int, Column]] = {
|
699
|
-
"UID": (
|
700
|
-
0,
|
701
|
-
Column(
|
702
|
-
"[bold white]UID",
|
703
|
-
footer=f"[white]{metadata_info['total_neurons']}[/white]",
|
704
|
-
style="white",
|
705
|
-
justify="right",
|
706
|
-
ratio=0.75,
|
707
|
-
),
|
708
|
-
),
|
709
|
-
"STAKE": (
|
710
|
-
1,
|
711
|
-
Column(
|
712
|
-
"[bold white]STAKE(\u03c4)",
|
713
|
-
footer=metadata_info["total_stake"],
|
714
|
-
style="bright_cyan",
|
715
|
-
justify="right",
|
716
|
-
no_wrap=True,
|
717
|
-
ratio=1.5,
|
718
|
-
),
|
719
|
-
),
|
720
|
-
"RANK": (
|
721
|
-
2,
|
722
|
-
Column(
|
723
|
-
"[bold white]RANK",
|
724
|
-
footer=metadata_info["rank"],
|
725
|
-
style="medium_purple",
|
726
|
-
justify="right",
|
727
|
-
no_wrap=True,
|
728
|
-
ratio=1,
|
729
|
-
),
|
730
|
-
),
|
731
|
-
"TRUST": (
|
732
|
-
3,
|
733
|
-
Column(
|
734
|
-
"[bold white]TRUST",
|
735
|
-
footer=metadata_info["trust"],
|
736
|
-
style="dark_sea_green",
|
737
|
-
justify="right",
|
738
|
-
no_wrap=True,
|
739
|
-
ratio=1,
|
740
|
-
),
|
741
|
-
),
|
742
|
-
"CONSENSUS": (
|
743
|
-
4,
|
744
|
-
Column(
|
745
|
-
"[bold white]CONSENSUS",
|
746
|
-
footer=metadata_info["consensus"],
|
747
|
-
style="rgb(42,161,152)",
|
748
|
-
justify="right",
|
749
|
-
no_wrap=True,
|
750
|
-
ratio=1,
|
751
|
-
),
|
752
|
-
),
|
753
|
-
"INCENTIVE": (
|
754
|
-
5,
|
755
|
-
Column(
|
756
|
-
"[bold white]INCENTIVE",
|
757
|
-
footer=metadata_info["incentive"],
|
758
|
-
style="#5fd7ff",
|
759
|
-
justify="right",
|
760
|
-
no_wrap=True,
|
761
|
-
ratio=1,
|
762
|
-
),
|
763
|
-
),
|
764
|
-
"DIVIDENDS": (
|
765
|
-
6,
|
766
|
-
Column(
|
767
|
-
"[bold white]DIVIDENDS",
|
768
|
-
footer=metadata_info["dividends"],
|
769
|
-
style="#8787d7",
|
770
|
-
justify="right",
|
771
|
-
no_wrap=True,
|
772
|
-
ratio=1,
|
773
|
-
),
|
774
|
-
),
|
775
|
-
"EMISSION": (
|
776
|
-
7,
|
777
|
-
Column(
|
778
|
-
"[bold white]EMISSION(\u03c1)",
|
779
|
-
footer=metadata_info["emission"],
|
780
|
-
style="#d7d7ff",
|
781
|
-
justify="right",
|
782
|
-
no_wrap=True,
|
783
|
-
ratio=1.5,
|
784
|
-
),
|
785
|
-
),
|
786
|
-
"VTRUST": (
|
787
|
-
8,
|
788
|
-
Column(
|
789
|
-
"[bold white]VTRUST",
|
790
|
-
footer=metadata_info["validator_trust"],
|
791
|
-
style="magenta",
|
792
|
-
justify="right",
|
793
|
-
no_wrap=True,
|
794
|
-
ratio=1,
|
795
|
-
),
|
796
|
-
),
|
797
|
-
"VAL": (
|
798
|
-
9,
|
799
|
-
Column(
|
800
|
-
"[bold white]VAL",
|
801
|
-
justify="center",
|
802
|
-
style="bright_white",
|
803
|
-
no_wrap=True,
|
804
|
-
ratio=0.4,
|
805
|
-
),
|
806
|
-
),
|
807
|
-
"UPDATED": (
|
808
|
-
10,
|
809
|
-
Column("[bold white]UPDATED", justify="right", no_wrap=True, ratio=1),
|
810
|
-
),
|
811
|
-
"ACTIVE": (
|
812
|
-
11,
|
813
|
-
Column(
|
814
|
-
"[bold white]ACTIVE",
|
815
|
-
justify="center",
|
816
|
-
style="#8787ff",
|
817
|
-
no_wrap=True,
|
818
|
-
ratio=1,
|
819
|
-
),
|
820
|
-
),
|
821
|
-
"AXON": (
|
822
|
-
12,
|
823
|
-
Column(
|
824
|
-
"[bold white]AXON",
|
825
|
-
justify="left",
|
826
|
-
style="dark_orange",
|
827
|
-
overflow="fold",
|
828
|
-
ratio=2,
|
829
|
-
),
|
830
|
-
),
|
831
|
-
"HOTKEY": (
|
832
|
-
13,
|
833
|
-
Column(
|
834
|
-
"[bold white]HOTKEY",
|
835
|
-
justify="center",
|
836
|
-
style="bright_magenta",
|
837
|
-
overflow="fold",
|
838
|
-
ratio=1.5,
|
839
|
-
),
|
840
|
-
),
|
841
|
-
"COLDKEY": (
|
842
|
-
14,
|
843
|
-
Column(
|
844
|
-
"[bold white]COLDKEY",
|
845
|
-
justify="center",
|
846
|
-
style="bright_magenta",
|
847
|
-
overflow="fold",
|
848
|
-
ratio=1.5,
|
849
|
-
),
|
850
|
-
),
|
851
|
-
}
|
852
|
-
table_cols: list[Column] = []
|
853
|
-
table_cols_indices: list[int] = []
|
854
|
-
for k, (idx, v) in cols.items():
|
855
|
-
if display_cols[k] is True:
|
856
|
-
table_cols_indices.append(idx)
|
857
|
-
table_cols.append(v)
|
858
|
-
|
859
|
-
table = Table(
|
860
|
-
*table_cols,
|
861
|
-
show_footer=True,
|
862
|
-
show_edge=False,
|
863
|
-
header_style="bold white",
|
864
|
-
border_style="bright_black",
|
865
|
-
style="bold",
|
866
|
-
title_style="bold white",
|
867
|
-
title_justify="center",
|
868
|
-
show_lines=False,
|
869
|
-
expand=True,
|
870
|
-
title=(
|
871
|
-
f"[underline dark_orange]Metagraph[/underline dark_orange]\n\n"
|
872
|
-
f"Net: [bright_cyan]{metadata_info['net']}[/bright_cyan], "
|
873
|
-
f"Block: [bright_cyan]{metadata_info['block']}[/bright_cyan], "
|
874
|
-
f"N: [bright_green]{metadata_info['N0']}[/bright_green]/[bright_red]{metadata_info['N1']}[/bright_red], "
|
875
|
-
f"Stake: [dark_orange]{metadata_info['stake']}[/dark_orange], "
|
876
|
-
f"Issuance: [bright_blue]{metadata_info['issuance']}[/bright_blue], "
|
877
|
-
f"Difficulty: [bright_cyan]{metadata_info['difficulty']}[/bright_cyan]\n"
|
878
|
-
),
|
879
|
-
pad_edge=True,
|
880
|
-
)
|
881
|
-
|
882
|
-
if all(x is False for x in display_cols.values()):
|
883
|
-
console.print("You have selected no columns to display in your config.")
|
884
|
-
table.add_row(" " * 256) # allows title to be printed
|
885
|
-
elif any(x is False for x in display_cols.values()):
|
886
|
-
console.print(
|
887
|
-
"Limiting column display output based on your config settings. Hiding columns "
|
888
|
-
f"{', '.join([k for (k, v) in display_cols.items() if v is False])}"
|
889
|
-
)
|
890
|
-
for row in table_data:
|
891
|
-
new_row = [row[idx] for idx in table_cols_indices]
|
892
|
-
table.add_row(*new_row)
|
893
|
-
else:
|
894
|
-
for row in table_data:
|
895
|
-
table.add_row(*row)
|
896
|
-
|
897
|
-
console.print(table)
|