meshtensor-cli 9.18.1__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.
- meshtensor_cli/__init__.py +22 -0
- meshtensor_cli/cli.py +10742 -0
- meshtensor_cli/doc_generation_helper.py +4 -0
- meshtensor_cli/src/__init__.py +1085 -0
- meshtensor_cli/src/commands/__init__.py +0 -0
- meshtensor_cli/src/commands/axon/__init__.py +0 -0
- meshtensor_cli/src/commands/axon/axon.py +132 -0
- meshtensor_cli/src/commands/crowd/__init__.py +0 -0
- meshtensor_cli/src/commands/crowd/contribute.py +621 -0
- meshtensor_cli/src/commands/crowd/contributors.py +200 -0
- meshtensor_cli/src/commands/crowd/create.py +783 -0
- meshtensor_cli/src/commands/crowd/dissolve.py +219 -0
- meshtensor_cli/src/commands/crowd/refund.py +233 -0
- meshtensor_cli/src/commands/crowd/update.py +418 -0
- meshtensor_cli/src/commands/crowd/utils.py +124 -0
- meshtensor_cli/src/commands/crowd/view.py +991 -0
- meshtensor_cli/src/commands/governance/__init__.py +0 -0
- meshtensor_cli/src/commands/governance/governance.py +794 -0
- meshtensor_cli/src/commands/liquidity/__init__.py +0 -0
- meshtensor_cli/src/commands/liquidity/liquidity.py +699 -0
- meshtensor_cli/src/commands/liquidity/utils.py +202 -0
- meshtensor_cli/src/commands/proxy.py +700 -0
- meshtensor_cli/src/commands/stake/__init__.py +0 -0
- meshtensor_cli/src/commands/stake/add.py +799 -0
- meshtensor_cli/src/commands/stake/auto_staking.py +306 -0
- meshtensor_cli/src/commands/stake/children_hotkeys.py +865 -0
- meshtensor_cli/src/commands/stake/claim.py +770 -0
- meshtensor_cli/src/commands/stake/list.py +738 -0
- meshtensor_cli/src/commands/stake/move.py +1211 -0
- meshtensor_cli/src/commands/stake/remove.py +1466 -0
- meshtensor_cli/src/commands/stake/wizard.py +323 -0
- meshtensor_cli/src/commands/subnets/__init__.py +0 -0
- meshtensor_cli/src/commands/subnets/mechanisms.py +515 -0
- meshtensor_cli/src/commands/subnets/price.py +733 -0
- meshtensor_cli/src/commands/subnets/subnets.py +2908 -0
- meshtensor_cli/src/commands/sudo.py +1294 -0
- meshtensor_cli/src/commands/tc/__init__.py +0 -0
- meshtensor_cli/src/commands/tc/tc.py +190 -0
- meshtensor_cli/src/commands/treasury/__init__.py +0 -0
- meshtensor_cli/src/commands/treasury/treasury.py +194 -0
- meshtensor_cli/src/commands/view.py +354 -0
- meshtensor_cli/src/commands/wallets.py +2311 -0
- meshtensor_cli/src/commands/weights.py +467 -0
- meshtensor_cli/src/meshtensor/__init__.py +0 -0
- meshtensor_cli/src/meshtensor/balances.py +313 -0
- meshtensor_cli/src/meshtensor/chain_data.py +1263 -0
- meshtensor_cli/src/meshtensor/extrinsics/__init__.py +0 -0
- meshtensor_cli/src/meshtensor/extrinsics/mev_shield.py +174 -0
- meshtensor_cli/src/meshtensor/extrinsics/registration.py +1861 -0
- meshtensor_cli/src/meshtensor/extrinsics/root.py +550 -0
- meshtensor_cli/src/meshtensor/extrinsics/serving.py +255 -0
- meshtensor_cli/src/meshtensor/extrinsics/transfer.py +239 -0
- meshtensor_cli/src/meshtensor/meshtensor_interface.py +2598 -0
- meshtensor_cli/src/meshtensor/minigraph.py +254 -0
- meshtensor_cli/src/meshtensor/networking.py +12 -0
- meshtensor_cli/src/meshtensor/templates/main-filters.j2 +24 -0
- meshtensor_cli/src/meshtensor/templates/main-header.j2 +36 -0
- meshtensor_cli/src/meshtensor/templates/neuron-details.j2 +111 -0
- meshtensor_cli/src/meshtensor/templates/price-multi.j2 +113 -0
- meshtensor_cli/src/meshtensor/templates/price-single.j2 +99 -0
- meshtensor_cli/src/meshtensor/templates/subnet-details-header.j2 +49 -0
- meshtensor_cli/src/meshtensor/templates/subnet-details.j2 +32 -0
- meshtensor_cli/src/meshtensor/templates/subnet-metrics.j2 +57 -0
- meshtensor_cli/src/meshtensor/templates/subnets-table.j2 +28 -0
- meshtensor_cli/src/meshtensor/templates/table.j2 +267 -0
- meshtensor_cli/src/meshtensor/templates/view.css +1058 -0
- meshtensor_cli/src/meshtensor/templates/view.j2 +43 -0
- meshtensor_cli/src/meshtensor/templates/view.js +1053 -0
- meshtensor_cli/src/meshtensor/utils.py +2007 -0
- meshtensor_cli/version.py +23 -0
- meshtensor_cli-9.18.1.dist-info/METADATA +261 -0
- meshtensor_cli-9.18.1.dist-info/RECORD +74 -0
- meshtensor_cli-9.18.1.dist-info/WHEEL +4 -0
- meshtensor_cli-9.18.1.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
# The MIT License (MIT)
|
|
2
|
+
# Copyright © 2021 Yuma Rao
|
|
3
|
+
# Copyright © 2023 Opentensor Foundation
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
|
5
|
+
# documentation files (the “Software”), to deal in the Software without restriction, including without limitation
|
|
6
|
+
# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
|
7
|
+
# and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
8
|
+
|
|
9
|
+
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
|
10
|
+
# the Software.
|
|
11
|
+
|
|
12
|
+
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
13
|
+
# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
14
|
+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
15
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
16
|
+
# DEALINGS IN THE SOFTWARE.
|
|
17
|
+
|
|
18
|
+
import asyncio
|
|
19
|
+
import hashlib
|
|
20
|
+
from typing import Union, List, TYPE_CHECKING, Optional
|
|
21
|
+
|
|
22
|
+
from meshtensor_wallet import Wallet, Keypair
|
|
23
|
+
import numpy as np
|
|
24
|
+
from numpy.typing import NDArray
|
|
25
|
+
from rich.table import Table, Column
|
|
26
|
+
from scalecodec import ScaleBytes, U16, Vec
|
|
27
|
+
from async_substrate_interface.errors import SubstrateRequestException
|
|
28
|
+
|
|
29
|
+
from meshtensor_cli.src.meshtensor.meshtensor_interface import MeshtensorInterface
|
|
30
|
+
from meshtensor_cli.src.meshtensor.extrinsics.registration import is_hotkey_registered
|
|
31
|
+
from meshtensor_cli.src.meshtensor.utils import (
|
|
32
|
+
confirm_action,
|
|
33
|
+
console,
|
|
34
|
+
print_error,
|
|
35
|
+
print_success,
|
|
36
|
+
u16_normalized_float,
|
|
37
|
+
print_verbose,
|
|
38
|
+
format_error_message,
|
|
39
|
+
unlock_key,
|
|
40
|
+
get_hotkey_pub_ss58,
|
|
41
|
+
print_extrinsic_id,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
if TYPE_CHECKING:
|
|
45
|
+
from meshtensor_cli.src.meshtensor.minigraph import MiniGraph
|
|
46
|
+
|
|
47
|
+
U32_MAX = 4294967295
|
|
48
|
+
U16_MAX = 65535
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
async def get_current_weights_for_uid(
|
|
52
|
+
meshtensor: MeshtensorInterface,
|
|
53
|
+
netuid: int,
|
|
54
|
+
uid: int,
|
|
55
|
+
) -> dict[int, float]:
|
|
56
|
+
"""
|
|
57
|
+
Fetches the current weights set by a specific UID on a subnet.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
meshtensor: The MeshtensorInterface instance.
|
|
61
|
+
netuid: The network UID (0 for root network).
|
|
62
|
+
uid: The UID of the neuron whose weights to fetch.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
A dictionary mapping destination netuid to normalized weight (0.0-1.0).
|
|
66
|
+
"""
|
|
67
|
+
weights_data = await meshtensor.weights(netuid=netuid)
|
|
68
|
+
current_weights: dict[int, float] = {}
|
|
69
|
+
|
|
70
|
+
for validator_uid, weight_list in weights_data:
|
|
71
|
+
if validator_uid == uid:
|
|
72
|
+
for dest_netuid, raw_weight in weight_list:
|
|
73
|
+
current_weights[dest_netuid] = u16_normalized_float(raw_weight)
|
|
74
|
+
break
|
|
75
|
+
|
|
76
|
+
return current_weights
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
async def get_limits(meshtensor: MeshtensorInterface) -> tuple[int, float]:
|
|
80
|
+
# Get weight restrictions.
|
|
81
|
+
maw, mwl = await asyncio.gather(
|
|
82
|
+
meshtensor.get_hyperparameter("MinAllowedWeights", netuid=0),
|
|
83
|
+
meshtensor.get_hyperparameter("MaxWeightsLimit", netuid=0),
|
|
84
|
+
)
|
|
85
|
+
min_allowed_weights = int(maw)
|
|
86
|
+
max_weight_limit = u16_normalized_float(int(mwl))
|
|
87
|
+
return min_allowed_weights, max_weight_limit
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def normalize_max_weight(
|
|
91
|
+
x: NDArray[np.float32], limit: float = 0.1
|
|
92
|
+
) -> NDArray[np.float32]:
|
|
93
|
+
"""
|
|
94
|
+
Normalizes the tensor x so that sum(x) = 1 and the max value is not greater than the limit.
|
|
95
|
+
|
|
96
|
+
:param x: Tensor to be max_value normalized.
|
|
97
|
+
:param limit: Max value after normalization.
|
|
98
|
+
|
|
99
|
+
:return: Normalized x tensor.
|
|
100
|
+
"""
|
|
101
|
+
epsilon = 1e-7 # For numerical stability after normalization
|
|
102
|
+
|
|
103
|
+
weights = x.copy()
|
|
104
|
+
values = np.sort(weights)
|
|
105
|
+
|
|
106
|
+
if x.sum() == 0 or x.shape[0] * limit <= 1:
|
|
107
|
+
return np.ones_like(x) / x.shape[0]
|
|
108
|
+
else:
|
|
109
|
+
estimation = values / values.sum()
|
|
110
|
+
|
|
111
|
+
if estimation.max() <= limit:
|
|
112
|
+
return weights / weights.sum()
|
|
113
|
+
|
|
114
|
+
# Find the cumulative sum and sorted tensor
|
|
115
|
+
cumsum = np.cumsum(estimation, 0)
|
|
116
|
+
|
|
117
|
+
# Determine the index of cutoff
|
|
118
|
+
estimation_sum = np.array(
|
|
119
|
+
[(len(values) - i - 1) * estimation[i] for i in range(len(values))]
|
|
120
|
+
)
|
|
121
|
+
n_values = (estimation / (estimation_sum + cumsum + epsilon) < limit).sum()
|
|
122
|
+
|
|
123
|
+
# Determine the cutoff based on the index
|
|
124
|
+
cutoff_scale = (limit * cumsum[n_values - 1] - epsilon) / (
|
|
125
|
+
1 - (limit * (len(estimation) - n_values))
|
|
126
|
+
)
|
|
127
|
+
cutoff = cutoff_scale * values.sum()
|
|
128
|
+
|
|
129
|
+
# Applying the cutoff
|
|
130
|
+
weights[weights > cutoff] = cutoff
|
|
131
|
+
|
|
132
|
+
y = weights / weights.sum()
|
|
133
|
+
|
|
134
|
+
return y
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def convert_weights_and_uids_for_emit(
|
|
138
|
+
uids: NDArray[np.int64],
|
|
139
|
+
weights: NDArray[np.float32],
|
|
140
|
+
) -> tuple[List[int], List[int]]:
|
|
141
|
+
"""Converts weights into integer u32 representation that sum to MAX_INT_WEIGHT.
|
|
142
|
+
|
|
143
|
+
:param uids: Tensor of uids as destinations for passed weights.
|
|
144
|
+
:param weights: Tensor of weights.
|
|
145
|
+
|
|
146
|
+
:return: (weight_uids, weight_vals)
|
|
147
|
+
"""
|
|
148
|
+
# Checks.
|
|
149
|
+
weights = weights.tolist()
|
|
150
|
+
uids = uids.tolist()
|
|
151
|
+
if min(weights) < 0:
|
|
152
|
+
raise ValueError(
|
|
153
|
+
"Passed weight is negative cannot exist on chain {}".format(weights)
|
|
154
|
+
)
|
|
155
|
+
if min(uids) < 0:
|
|
156
|
+
raise ValueError("Passed uid is negative cannot exist on chain {}".format(uids))
|
|
157
|
+
if len(uids) != len(weights):
|
|
158
|
+
raise ValueError(
|
|
159
|
+
"Passed weights and uids must have the same length, got {} and {}".format(
|
|
160
|
+
len(uids), len(weights)
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
if sum(weights) == 0:
|
|
164
|
+
return [], [] # Nothing to set on chain.
|
|
165
|
+
else:
|
|
166
|
+
max_weight = float(max(weights))
|
|
167
|
+
weights = [
|
|
168
|
+
float(value) / max_weight for value in weights
|
|
169
|
+
] # max-upscale values (max_weight = 1).
|
|
170
|
+
|
|
171
|
+
weight_vals = []
|
|
172
|
+
weight_uids = []
|
|
173
|
+
for i, (weight_i, uid_i) in enumerate(list(zip(weights, uids))):
|
|
174
|
+
uint16_val = round(
|
|
175
|
+
float(weight_i) * int(U16_MAX)
|
|
176
|
+
) # convert to int representation.
|
|
177
|
+
|
|
178
|
+
# Filter zeros
|
|
179
|
+
if uint16_val != 0: # Filter zeros
|
|
180
|
+
weight_vals.append(uint16_val)
|
|
181
|
+
weight_uids.append(uid_i)
|
|
182
|
+
|
|
183
|
+
return weight_uids, weight_vals
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
async def process_weights_for_netuid(
|
|
187
|
+
uids: NDArray[np.int64],
|
|
188
|
+
weights: NDArray[np.float32],
|
|
189
|
+
netuid: int,
|
|
190
|
+
meshtensor: MeshtensorInterface,
|
|
191
|
+
metagraph: "MiniGraph" = None,
|
|
192
|
+
exclude_quantile: int = 0,
|
|
193
|
+
) -> tuple[NDArray[np.int64], NDArray[np.float32]]:
|
|
194
|
+
# meshtensor.logging.debug("process_weights_for_netuid()")
|
|
195
|
+
# meshtensor.logging.debug("weights", weights)
|
|
196
|
+
# meshtensor.logging.debug("netuid", netuid)
|
|
197
|
+
# meshtensor.logging.debug("meshtensor", meshtensor)
|
|
198
|
+
# meshtensor.logging.debug("metagraph", metagraph)
|
|
199
|
+
|
|
200
|
+
# Get latest metagraph from chain if metagraph is None.
|
|
201
|
+
if metagraph is None:
|
|
202
|
+
metagraph = meshtensor.metagraph(netuid)
|
|
203
|
+
|
|
204
|
+
if not isinstance(weights, np.float32):
|
|
205
|
+
weights = weights.astype(np.float32)
|
|
206
|
+
|
|
207
|
+
# Network configuration parameters from a meshtensor.
|
|
208
|
+
# These parameters determine the range of acceptable weights for each neuron.
|
|
209
|
+
quantile = exclude_quantile / U16_MAX
|
|
210
|
+
min_allowed_weights, max_weight_limit = await get_limits(meshtensor)
|
|
211
|
+
# meshtensor.logging.debug("quantile", quantile)
|
|
212
|
+
# meshtensor.logging.debug("min_allowed_weights", min_allowed_weights)
|
|
213
|
+
# meshtensor.logging.debug("max_weight_limit", max_weight_limit)
|
|
214
|
+
|
|
215
|
+
# Find all non zero weights.
|
|
216
|
+
non_zero_weight_idx = np.argwhere(weights > 0).squeeze(axis=1)
|
|
217
|
+
non_zero_weight_uids = uids[non_zero_weight_idx]
|
|
218
|
+
non_zero_weights = weights[non_zero_weight_idx]
|
|
219
|
+
nzw_size = non_zero_weights.size
|
|
220
|
+
if nzw_size == 0 or metagraph.n < min_allowed_weights:
|
|
221
|
+
# meshtensor.logging.warning("No non-zero weights returning all ones.")
|
|
222
|
+
final_weights = np.ones(metagraph.n, dtype=np.int64) / metagraph.n
|
|
223
|
+
# meshtensor.logging.debug("final_weights", final_weights)
|
|
224
|
+
final_weights_count = np.arange(len(final_weights))
|
|
225
|
+
return final_weights_count, final_weights
|
|
226
|
+
|
|
227
|
+
elif nzw_size < min_allowed_weights:
|
|
228
|
+
# meshtensor.logging.warning(
|
|
229
|
+
# "No non-zero weights less than min allowed weight, returning all ones."
|
|
230
|
+
# )
|
|
231
|
+
weights = (
|
|
232
|
+
np.ones(metagraph.n, dtype=np.int64) * 1e-5
|
|
233
|
+
) # creating minimum even non-zero weights
|
|
234
|
+
weights[non_zero_weight_idx] += non_zero_weights
|
|
235
|
+
# meshtensor.logging.debug("final_weights", weights)
|
|
236
|
+
normalized_weights = normalize_max_weight(x=weights, limit=max_weight_limit)
|
|
237
|
+
nw_arange = np.arange(len(normalized_weights))
|
|
238
|
+
return nw_arange, normalized_weights
|
|
239
|
+
|
|
240
|
+
# meshtensor.logging.debug("non_zero_weights", non_zero_weights)
|
|
241
|
+
|
|
242
|
+
# Compute the exclude quantile and find the weights in the lowest quantile
|
|
243
|
+
max_exclude = max(0, len(non_zero_weights) - min_allowed_weights) / len(
|
|
244
|
+
non_zero_weights
|
|
245
|
+
)
|
|
246
|
+
exclude_quantile = min([quantile, max_exclude])
|
|
247
|
+
lowest_quantile = np.quantile(non_zero_weights, exclude_quantile)
|
|
248
|
+
# meshtensor.logging.debug("max_exclude", max_exclude)
|
|
249
|
+
# meshtensor.logging.debug("exclude_quantile", exclude_quantile)
|
|
250
|
+
# meshtensor.logging.debug("lowest_quantile", lowest_quantile)
|
|
251
|
+
|
|
252
|
+
# Exclude all weights below the allowed quantile.
|
|
253
|
+
non_zero_weight_uids = non_zero_weight_uids[lowest_quantile <= non_zero_weights]
|
|
254
|
+
non_zero_weights = non_zero_weights[lowest_quantile <= non_zero_weights]
|
|
255
|
+
# meshtensor.logging.debug("non_zero_weight_uids", non_zero_weight_uids)
|
|
256
|
+
# meshtensor.logging.debug("non_zero_weights", non_zero_weights)
|
|
257
|
+
|
|
258
|
+
# Normalize weights and return.
|
|
259
|
+
normalized_weights = normalize_max_weight(
|
|
260
|
+
x=non_zero_weights, limit=max_weight_limit
|
|
261
|
+
)
|
|
262
|
+
# meshtensor.logging.debug("final_weights", normalized_weights)
|
|
263
|
+
|
|
264
|
+
return non_zero_weight_uids, normalized_weights
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def generate_weight_hash(
|
|
268
|
+
address: str,
|
|
269
|
+
netuid: int,
|
|
270
|
+
uids: List[int],
|
|
271
|
+
values: List[int],
|
|
272
|
+
version_key: int,
|
|
273
|
+
salt: List[int],
|
|
274
|
+
) -> str:
|
|
275
|
+
"""
|
|
276
|
+
Generate a valid commit hash from the provided weights.
|
|
277
|
+
|
|
278
|
+
:param address: The account identifier. Wallet ss58_address.
|
|
279
|
+
:param netuid: The network unique identifier.
|
|
280
|
+
:param uids: The list of UIDs.
|
|
281
|
+
:param salt: The salt to add to hash.
|
|
282
|
+
:param values: The list of weight values.
|
|
283
|
+
:param version_key: The version key.
|
|
284
|
+
|
|
285
|
+
:return The generated commit hash.
|
|
286
|
+
"""
|
|
287
|
+
# Encode data using SCALE codec
|
|
288
|
+
wallet_address = ScaleBytes(Keypair(ss58_address=address).public_key)
|
|
289
|
+
netuid = ScaleBytes(netuid.to_bytes(2, "little"))
|
|
290
|
+
|
|
291
|
+
vec_uids = Vec(data=None, sub_type="U16")
|
|
292
|
+
vec_uids.value = [U16(ScaleBytes(uid.to_bytes(2, "little"))) for uid in uids]
|
|
293
|
+
uids = ScaleBytes(vec_uids.encode().data)
|
|
294
|
+
|
|
295
|
+
vec_values = Vec(data=None, sub_type="U16")
|
|
296
|
+
vec_values.value = [
|
|
297
|
+
U16(ScaleBytes(value.to_bytes(2, "little"))) for value in values
|
|
298
|
+
]
|
|
299
|
+
values = ScaleBytes(vec_values.encode().data)
|
|
300
|
+
|
|
301
|
+
version_key = ScaleBytes(version_key.to_bytes(8, "little"))
|
|
302
|
+
|
|
303
|
+
vec_salt = Vec(data=None, sub_type="U16")
|
|
304
|
+
vec_salt.value = [U16(ScaleBytes(salts.to_bytes(2, "little"))) for salts in salt]
|
|
305
|
+
salt = ScaleBytes(vec_salt.encode().data)
|
|
306
|
+
|
|
307
|
+
data = wallet_address + netuid + uids + values + salt + version_key
|
|
308
|
+
|
|
309
|
+
# Generate Blake2b hash of the data tuple
|
|
310
|
+
blake2b_hash = hashlib.blake2b(data.data, digest_size=32)
|
|
311
|
+
|
|
312
|
+
# Convert the hash to hex string and add "0x" prefix
|
|
313
|
+
commit_hash = "0x" + blake2b_hash.hexdigest()
|
|
314
|
+
|
|
315
|
+
return commit_hash
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
async def root_register_extrinsic(
|
|
319
|
+
meshtensor: MeshtensorInterface,
|
|
320
|
+
wallet: Wallet,
|
|
321
|
+
wait_for_inclusion: bool = True,
|
|
322
|
+
wait_for_finalization: bool = True,
|
|
323
|
+
proxy: Optional[str] = None,
|
|
324
|
+
) -> tuple[bool, str, Optional[str]]:
|
|
325
|
+
r"""Registers the wallet to root network.
|
|
326
|
+
|
|
327
|
+
:param meshtensor: The MeshtensorInterface object
|
|
328
|
+
:param wallet: Meshtensor wallet object.
|
|
329
|
+
:param wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`, or returns
|
|
330
|
+
`False` if the extrinsic fails to enter the block within the timeout.
|
|
331
|
+
:param wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`,
|
|
332
|
+
or returns `False` if the extrinsic fails to be finalized within the timeout.
|
|
333
|
+
:param proxy: Optional proxy to use for making the call.
|
|
334
|
+
|
|
335
|
+
:return: (success, msg), with success being `True` if extrinsic was finalized or included in the block. If we did
|
|
336
|
+
not wait for finalization/inclusion, the response is `True`.
|
|
337
|
+
"""
|
|
338
|
+
|
|
339
|
+
if not (unlock := unlock_key(wallet)).success:
|
|
340
|
+
return False, unlock.message, None
|
|
341
|
+
|
|
342
|
+
print_verbose(f"Checking if hotkey ({wallet.hotkey_str}) is registered on root")
|
|
343
|
+
is_registered = await is_hotkey_registered(
|
|
344
|
+
meshtensor, netuid=0, hotkey_ss58=get_hotkey_pub_ss58(wallet)
|
|
345
|
+
)
|
|
346
|
+
if is_registered:
|
|
347
|
+
print_success("Already registered on root network.")
|
|
348
|
+
return True, "Already registered on root network", None
|
|
349
|
+
|
|
350
|
+
with console.status(":satellite: Registering to root network...", spinner="earth"):
|
|
351
|
+
call = await meshtensor.substrate.compose_call(
|
|
352
|
+
call_module="MeshtensorModule",
|
|
353
|
+
call_function="root_register",
|
|
354
|
+
call_params={"hotkey": get_hotkey_pub_ss58(wallet)},
|
|
355
|
+
)
|
|
356
|
+
success, err_msg, ext_receipt = await meshtensor.sign_and_send_extrinsic(
|
|
357
|
+
call,
|
|
358
|
+
wallet=wallet,
|
|
359
|
+
wait_for_inclusion=wait_for_inclusion,
|
|
360
|
+
wait_for_finalization=wait_for_finalization,
|
|
361
|
+
proxy=proxy,
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
if not success:
|
|
365
|
+
print_error(f"Failed: {err_msg}")
|
|
366
|
+
await asyncio.sleep(0.5)
|
|
367
|
+
return False, err_msg, None
|
|
368
|
+
|
|
369
|
+
# Successful registration, final check for neuron and pubkey
|
|
370
|
+
else:
|
|
371
|
+
ext_id = await ext_receipt.get_extrinsic_identifier()
|
|
372
|
+
await print_extrinsic_id(ext_receipt)
|
|
373
|
+
uid = await meshtensor.query(
|
|
374
|
+
module="MeshtensorModule",
|
|
375
|
+
storage_function="Uids",
|
|
376
|
+
params=[0, get_hotkey_pub_ss58(wallet)],
|
|
377
|
+
)
|
|
378
|
+
if uid is not None:
|
|
379
|
+
print_success(f"Registered with UID {uid}")
|
|
380
|
+
return True, f"Registered with UID {uid}", ext_id
|
|
381
|
+
else:
|
|
382
|
+
# neuron not found, try again
|
|
383
|
+
print_error("Unknown error. Neuron not found.")
|
|
384
|
+
return False, "Unknown error. Neuron not found.", ext_id
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
async def set_root_weights_extrinsic(
|
|
388
|
+
meshtensor: MeshtensorInterface,
|
|
389
|
+
wallet: Wallet,
|
|
390
|
+
netuids: Union[NDArray[np.int64], list[int]],
|
|
391
|
+
weights: Union[NDArray[np.float32], list[float]],
|
|
392
|
+
version_key: int = 0,
|
|
393
|
+
wait_for_inclusion: bool = False,
|
|
394
|
+
wait_for_finalization: bool = False,
|
|
395
|
+
prompt: bool = False,
|
|
396
|
+
decline: bool = False,
|
|
397
|
+
quiet: bool = False,
|
|
398
|
+
) -> bool:
|
|
399
|
+
"""Sets the given weights and values on chain for wallet hotkey account.
|
|
400
|
+
|
|
401
|
+
:param meshtensor: The MeshtensorInterface object
|
|
402
|
+
:param wallet: Meshtensor wallet object.
|
|
403
|
+
:param netuids: The `netuid` of the subnet to set weights for.
|
|
404
|
+
:param weights: Weights to set. These must be `float` s and must correspond to the passed `netuid` s.
|
|
405
|
+
:param version_key: The version key of the validator.
|
|
406
|
+
:param wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`, or returns
|
|
407
|
+
`False` if the extrinsic fails to enter the block within the timeout.
|
|
408
|
+
:param wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`,
|
|
409
|
+
or returns `False` if the extrinsic fails to be finalized within the timeout.
|
|
410
|
+
:param prompt: If `True`, the call waits for confirmation from the user before proceeding.
|
|
411
|
+
:return: `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion,
|
|
412
|
+
the response is `True`.
|
|
413
|
+
"""
|
|
414
|
+
|
|
415
|
+
async def _do_set_weights():
|
|
416
|
+
call = await meshtensor.substrate.compose_call(
|
|
417
|
+
call_module="MeshtensorModule",
|
|
418
|
+
call_function="set_root_weights",
|
|
419
|
+
call_params={
|
|
420
|
+
"dests": weight_uids,
|
|
421
|
+
"weights": weight_vals,
|
|
422
|
+
"netuid": 0,
|
|
423
|
+
"version_key": version_key,
|
|
424
|
+
"hotkey": get_hotkey_pub_ss58(wallet),
|
|
425
|
+
},
|
|
426
|
+
)
|
|
427
|
+
# Period dictates how long the extrinsic will stay as part of waiting pool
|
|
428
|
+
extrinsic = await meshtensor.substrate.create_signed_extrinsic(
|
|
429
|
+
call=call,
|
|
430
|
+
keypair=wallet.coldkey,
|
|
431
|
+
era={"period": 5},
|
|
432
|
+
)
|
|
433
|
+
response = await meshtensor.substrate.submit_extrinsic(
|
|
434
|
+
extrinsic,
|
|
435
|
+
wait_for_inclusion=wait_for_inclusion,
|
|
436
|
+
wait_for_finalization=wait_for_finalization,
|
|
437
|
+
)
|
|
438
|
+
# We only wait here if we expect finalization.
|
|
439
|
+
if not wait_for_finalization and not wait_for_inclusion:
|
|
440
|
+
return True, "Not waiting for finalization or inclusion."
|
|
441
|
+
|
|
442
|
+
if await response.is_success:
|
|
443
|
+
return True, "Successfully set weights."
|
|
444
|
+
else:
|
|
445
|
+
return False, await response.error_message
|
|
446
|
+
|
|
447
|
+
my_uid = await meshtensor.query(
|
|
448
|
+
"MeshtensorModule", "Uids", [0, get_hotkey_pub_ss58(wallet)]
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
if my_uid is None:
|
|
452
|
+
print_error("Your hotkey is not registered to the root network")
|
|
453
|
+
return False
|
|
454
|
+
|
|
455
|
+
if not unlock_key(wallet).success:
|
|
456
|
+
return False
|
|
457
|
+
|
|
458
|
+
# First convert types.
|
|
459
|
+
if isinstance(netuids, list):
|
|
460
|
+
netuids = np.array(netuids, dtype=np.int64)
|
|
461
|
+
if isinstance(weights, list):
|
|
462
|
+
weights = np.array(weights, dtype=np.float32)
|
|
463
|
+
|
|
464
|
+
print_verbose("Fetching weight limits")
|
|
465
|
+
min_allowed_weights, max_weight_limit = await get_limits(meshtensor)
|
|
466
|
+
|
|
467
|
+
# Get non zero values.
|
|
468
|
+
non_zero_weight_idx = np.argwhere(weights > 0).squeeze(axis=1)
|
|
469
|
+
non_zero_weights = weights[non_zero_weight_idx]
|
|
470
|
+
if non_zero_weights.size < min_allowed_weights:
|
|
471
|
+
raise ValueError(
|
|
472
|
+
"The minimum number of weights required to set weights is {}, got {}".format(
|
|
473
|
+
min_allowed_weights, non_zero_weights.size
|
|
474
|
+
)
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
# Normalize the weights to max value.
|
|
478
|
+
print_verbose("Normalizing weights")
|
|
479
|
+
formatted_weights = normalize_max_weight(x=weights, limit=max_weight_limit)
|
|
480
|
+
console.print(
|
|
481
|
+
f"\nRaw weights -> Normalized weights: \n\t{weights} -> \n\t{formatted_weights}\n"
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
# Ask before moving on.
|
|
485
|
+
if prompt:
|
|
486
|
+
# Fetch current weights for comparison
|
|
487
|
+
print_verbose("Fetching current weights for comparison")
|
|
488
|
+
current_weights = await get_current_weights_for_uid(
|
|
489
|
+
meshtensor, netuid=0, uid=my_uid
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
table = Table(
|
|
493
|
+
Column("[dark_orange]Netuid", justify="center", style="bold green"),
|
|
494
|
+
Column("[dark_orange]Current", justify="center", style="dim"),
|
|
495
|
+
Column("[dark_orange]New", justify="center", style="bold light_goldenrod2"),
|
|
496
|
+
Column("[dark_orange]Change", justify="center"),
|
|
497
|
+
expand=False,
|
|
498
|
+
show_edge=False,
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
for netuid, new_weight in zip(netuids, formatted_weights):
|
|
502
|
+
current_weight = current_weights.get(netuid, 0.0)
|
|
503
|
+
diff = new_weight - current_weight
|
|
504
|
+
|
|
505
|
+
# Format the difference with color and sign
|
|
506
|
+
if diff > 0.00000001:
|
|
507
|
+
diff_str = f"[green]+{diff:.8f}[/green]"
|
|
508
|
+
elif diff < -0.00000001:
|
|
509
|
+
diff_str = f"[red]{diff:.8f}[/red]"
|
|
510
|
+
else:
|
|
511
|
+
diff_str = "[dim]0.00000000[/dim]"
|
|
512
|
+
|
|
513
|
+
table.add_row(
|
|
514
|
+
str(netuid),
|
|
515
|
+
f"{current_weight:.8f}",
|
|
516
|
+
f"{new_weight:.8f}",
|
|
517
|
+
diff_str,
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
console.print(table)
|
|
521
|
+
if not confirm_action(
|
|
522
|
+
"\nDo you want to set these root weights?",
|
|
523
|
+
decline=decline,
|
|
524
|
+
quiet=quiet,
|
|
525
|
+
):
|
|
526
|
+
return False
|
|
527
|
+
|
|
528
|
+
try:
|
|
529
|
+
with console.status("Setting root weights...", spinner="earth"):
|
|
530
|
+
weight_uids, weight_vals = convert_weights_and_uids_for_emit(
|
|
531
|
+
netuids, weights
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
success, error_message = await _do_set_weights()
|
|
535
|
+
|
|
536
|
+
if not wait_for_finalization and not wait_for_inclusion:
|
|
537
|
+
return True
|
|
538
|
+
|
|
539
|
+
if success is True:
|
|
540
|
+
print_success("Finalized")
|
|
541
|
+
return True
|
|
542
|
+
else:
|
|
543
|
+
fmt_err = format_error_message(error_message)
|
|
544
|
+
print_error(f"Failed: {fmt_err}")
|
|
545
|
+
return False
|
|
546
|
+
|
|
547
|
+
except SubstrateRequestException as e:
|
|
548
|
+
fmt_err = format_error_message(e)
|
|
549
|
+
print_error("Failed: error:{}".format(fmt_err))
|
|
550
|
+
return False
|