htcli 1.1.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.
- htcli-1.1.0.dist-info/METADATA +509 -0
- htcli-1.1.0.dist-info/RECORD +140 -0
- htcli-1.1.0.dist-info/WHEEL +4 -0
- htcli-1.1.0.dist-info/entry_points.txt +2 -0
- htcli-1.1.0.dist-info/licenses/LICENSE +21 -0
- src/__init__.py +0 -0
- src/htcli/__init__.py +5 -0
- src/htcli/client/__init__.py +338 -0
- src/htcli/client/extrinsics/__init__.py +26 -0
- src/htcli/client/extrinsics/base.py +487 -0
- src/htcli/client/extrinsics/consensus.py +79 -0
- src/htcli/client/extrinsics/governance.py +714 -0
- src/htcli/client/extrinsics/identity.py +490 -0
- src/htcli/client/extrinsics/node.py +1054 -0
- src/htcli/client/extrinsics/overwatch.py +401 -0
- src/htcli/client/extrinsics/staking.py +1504 -0
- src/htcli/client/extrinsics/subnet.py +2218 -0
- src/htcli/client/extrinsics/validator.py +203 -0
- src/htcli/client/extrinsics/wallet.py +323 -0
- src/htcli/client/offchain/__init__.py +10 -0
- src/htcli/client/offchain/backup.py +385 -0
- src/htcli/client/offchain/config.py +541 -0
- src/htcli/client/offchain/wallet.py +839 -0
- src/htcli/client/rpc/__init__.py +20 -0
- src/htcli/client/rpc/chain.py +568 -0
- src/htcli/client/rpc/node.py +783 -0
- src/htcli/client/rpc/overwatch.py +680 -0
- src/htcli/client/rpc/staking.py +216 -0
- src/htcli/client/rpc/subnet.py +2104 -0
- src/htcli/client/rpc/wallet.py +912 -0
- src/htcli/commands/__init__.py +31 -0
- src/htcli/commands/chain/__init__.py +66 -0
- src/htcli/commands/chain/display.py +204 -0
- src/htcli/commands/chain/handlers.py +260 -0
- src/htcli/commands/config/__init__.py +158 -0
- src/htcli/commands/config/display.py +353 -0
- src/htcli/commands/config/handlers.py +347 -0
- src/htcli/commands/config/prompts.py +357 -0
- src/htcli/commands/consensus/__init__.py +61 -0
- src/htcli/commands/consensus/handlers.py +100 -0
- src/htcli/commands/governance/__init__.py +49 -0
- src/htcli/commands/governance/handlers.py +81 -0
- src/htcli/commands/node/__init__.py +304 -0
- src/htcli/commands/node/display.py +749 -0
- src/htcli/commands/node/error_handling.py +470 -0
- src/htcli/commands/node/handlers.py +844 -0
- src/htcli/commands/node/prompts.py +346 -0
- src/htcli/commands/overwatch/__init__.py +219 -0
- src/htcli/commands/overwatch/display.py +396 -0
- src/htcli/commands/overwatch/error_handling.py +276 -0
- src/htcli/commands/overwatch/handlers.py +443 -0
- src/htcli/commands/overwatch/prompts.py +359 -0
- src/htcli/commands/stake/__init__.py +736 -0
- src/htcli/commands/stake/display.py +1103 -0
- src/htcli/commands/stake/error_handling.py +425 -0
- src/htcli/commands/stake/handlers.py +1902 -0
- src/htcli/commands/stake/prompts.py +1080 -0
- src/htcli/commands/subnet/__init__.py +639 -0
- src/htcli/commands/subnet/display.py +801 -0
- src/htcli/commands/subnet/error_handling.py +524 -0
- src/htcli/commands/subnet/handlers.py +2855 -0
- src/htcli/commands/subnet/prompts.py +1225 -0
- src/htcli/commands/validator/__init__.py +192 -0
- src/htcli/commands/validator/display.py +54 -0
- src/htcli/commands/validator/handlers.py +340 -0
- src/htcli/commands/wallet/__init__.py +546 -0
- src/htcli/commands/wallet/display.py +806 -0
- src/htcli/commands/wallet/error_handling.py +210 -0
- src/htcli/commands/wallet/handlers.py +3040 -0
- src/htcli/commands/wallet/prompts.py +1518 -0
- src/htcli/config.py +184 -0
- src/htcli/dependencies.py +186 -0
- src/htcli/errors/__init__.py +63 -0
- src/htcli/errors/base.py +141 -0
- src/htcli/errors/display.py +20 -0
- src/htcli/errors/handlers.py +710 -0
- src/htcli/main.py +343 -0
- src/htcli/models/__init__.py +21 -0
- src/htcli/models/enums/enum_types.py +35 -0
- src/htcli/models/errors.py +103 -0
- src/htcli/models/requests/__init__.py +197 -0
- src/htcli/models/requests/config.py +70 -0
- src/htcli/models/requests/consensus.py +19 -0
- src/htcli/models/requests/governance.py +38 -0
- src/htcli/models/requests/identity.py +51 -0
- src/htcli/models/requests/key.py +22 -0
- src/htcli/models/requests/node.py +91 -0
- src/htcli/models/requests/overwatch.py +64 -0
- src/htcli/models/requests/staking.py +580 -0
- src/htcli/models/requests/subnet.py +195 -0
- src/htcli/models/requests/validator.py +139 -0
- src/htcli/models/requests/wallet.py +118 -0
- src/htcli/models/responses/__init__.py +147 -0
- src/htcli/models/responses/base.py +18 -0
- src/htcli/models/responses/chain.py +39 -0
- src/htcli/models/responses/config.py +58 -0
- src/htcli/models/responses/identity.py +102 -0
- src/htcli/models/responses/overwatch.py +51 -0
- src/htcli/models/responses/staking.py +502 -0
- src/htcli/models/responses/subnet.py +856 -0
- src/htcli/models/responses/wallet.py +185 -0
- src/htcli/ui/__init__.py +87 -0
- src/htcli/ui/colors.py +309 -0
- src/htcli/ui/components/__init__.py +60 -0
- src/htcli/ui/components/panels.py +174 -0
- src/htcli/ui/components/progress.py +166 -0
- src/htcli/ui/components/spinners.py +92 -0
- src/htcli/ui/components/tables.py +809 -0
- src/htcli/ui/components/trees.py +721 -0
- src/htcli/ui/display.py +336 -0
- src/htcli/ui/prompts.py +870 -0
- src/htcli/utils/__init__.py +76 -0
- src/htcli/utils/blockchain/__init__.py +75 -0
- src/htcli/utils/blockchain/formatting.py +368 -0
- src/htcli/utils/blockchain/patches.py +286 -0
- src/htcli/utils/blockchain/peer_id.py +186 -0
- src/htcli/utils/blockchain/staking.py +448 -0
- src/htcli/utils/blockchain/type_registry.py +1373 -0
- src/htcli/utils/blockchain/validation.py +179 -0
- src/htcli/utils/cache.py +613 -0
- src/htcli/utils/constants.py +38 -0
- src/htcli/utils/legacy/__init__.py +12 -0
- src/htcli/utils/legacy/colors.py +311 -0
- src/htcli/utils/legacy/crypto.py +1176 -0
- src/htcli/utils/legacy/formatting.py +452 -0
- src/htcli/utils/legacy/interactive.py +306 -0
- src/htcli/utils/legacy/subnet_manifest.py +265 -0
- src/htcli/utils/legacy/validation.py +488 -0
- src/htcli/utils/logging.py +183 -0
- src/htcli/utils/network/__init__.py +20 -0
- src/htcli/utils/network/subnet.py +344 -0
- src/htcli/utils/prompts.py +27 -0
- src/htcli/utils/scale_codec.py +155 -0
- src/htcli/utils/validation/__init__.py +57 -0
- src/htcli/utils/validation/prompt_validators.py +267 -0
- src/htcli/utils/wallet/__init__.py +65 -0
- src/htcli/utils/wallet/auth.py +151 -0
- src/htcli/utils/wallet/core.py +1069 -0
- src/htcli/utils/wallet/crypto.py +1615 -0
- src/htcli/utils/wallet/migration.py +159 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Overwatch display functions for RPC-based operations and extrinsic operations.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, List, Optional
|
|
6
|
+
|
|
7
|
+
from ...ui.colors import amount, info, success
|
|
8
|
+
from ...ui.components import HTCLIPanel, HTCLITable
|
|
9
|
+
from ...ui.display import HTCLIConsole
|
|
10
|
+
|
|
11
|
+
console = HTCLIConsole()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def format_balance(balance: int) -> str:
|
|
15
|
+
"""Convert raw balance (wei) to TENSOR."""
|
|
16
|
+
return f"{balance / 1e18:,.4f}"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def format_peer_id(peer_id) -> str:
|
|
20
|
+
"""Format peer ID - show only if not empty."""
|
|
21
|
+
if not peer_id:
|
|
22
|
+
return "[dim]Not set[/dim]"
|
|
23
|
+
if isinstance(peer_id, bytes):
|
|
24
|
+
if len(peer_id) == 0:
|
|
25
|
+
return "[dim]Not set[/dim]"
|
|
26
|
+
try:
|
|
27
|
+
decoded = peer_id.decode('utf-8', errors='ignore')
|
|
28
|
+
return decoded if decoded else "[dim]Empty[/dim]"
|
|
29
|
+
except Exception:
|
|
30
|
+
return "[dim]Invalid[/dim]"
|
|
31
|
+
if isinstance(peer_id, str):
|
|
32
|
+
return peer_id if peer_id else "[dim]Not set[/dim]"
|
|
33
|
+
if isinstance(peer_id, list):
|
|
34
|
+
if len(peer_id) == 0:
|
|
35
|
+
return "[dim]Not set[/dim]"
|
|
36
|
+
try:
|
|
37
|
+
return format_peer_id(bytes(peer_id))
|
|
38
|
+
except Exception:
|
|
39
|
+
return "[dim]Invalid[/dim]"
|
|
40
|
+
return str(peer_id)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# ============================================================================
|
|
44
|
+
# RPC DISPLAY FUNCTIONS - Read Operations
|
|
45
|
+
# ============================================================================
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def display_overwatch_qualification(data: dict):
|
|
49
|
+
"""Display overwatch qualification check results.
|
|
50
|
+
|
|
51
|
+
Shows a detailed breakdown of requirements met and failed,
|
|
52
|
+
similar to subnet check-activation display.
|
|
53
|
+
"""
|
|
54
|
+
can_register = data.get("can_register", False)
|
|
55
|
+
requirements_met = data.get("requirements_met", [])
|
|
56
|
+
requirements_failed = data.get("requirements_failed", [])
|
|
57
|
+
details = data.get("details", {})
|
|
58
|
+
coldkey = data.get("coldkey", "Unknown")
|
|
59
|
+
|
|
60
|
+
# Truncate coldkey for display
|
|
61
|
+
# Use centralized formatter for consistency
|
|
62
|
+
from ...utils.wallet.crypto import format_address_display
|
|
63
|
+
coldkey_display = format_address_display(coldkey)
|
|
64
|
+
|
|
65
|
+
# Build content
|
|
66
|
+
if can_register:
|
|
67
|
+
status_line = "[htcli.success]✅ QUALIFIED - Can register as overwatch node[/htcli.success]"
|
|
68
|
+
border_style = "htcli.success"
|
|
69
|
+
title_emoji = "✅"
|
|
70
|
+
else:
|
|
71
|
+
status_line = "[htcli.error]❌ NOT QUALIFIED - Cannot register as overwatch node yet[/htcli.error]"
|
|
72
|
+
border_style = "htcli.error"
|
|
73
|
+
title_emoji = "❌"
|
|
74
|
+
|
|
75
|
+
content = f"""{status_line}
|
|
76
|
+
|
|
77
|
+
[htcli.accent]Coldkey:[/htcli.accent] [htcli.address]{coldkey_display}[/htcli.address]
|
|
78
|
+
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
# Requirements Met section
|
|
82
|
+
if requirements_met:
|
|
83
|
+
content += "[htcli.success]✅ Requirements Met:[/htcli.success]\n"
|
|
84
|
+
for req in requirements_met:
|
|
85
|
+
content += f" [htcli.success]•[/htcli.success] {req}\n"
|
|
86
|
+
content += "\n"
|
|
87
|
+
|
|
88
|
+
# Requirements Failed section
|
|
89
|
+
if requirements_failed:
|
|
90
|
+
content += "[htcli.error]❌ Requirements Not Met:[/htcli.error]\n"
|
|
91
|
+
for req in requirements_failed:
|
|
92
|
+
content += f" [htcli.error]•[/htcli.error] {req}\n"
|
|
93
|
+
content += "\n"
|
|
94
|
+
|
|
95
|
+
# Show network status details
|
|
96
|
+
content += "[htcli.accent]Network Status:[/htcli.accent]\n"
|
|
97
|
+
content += f" • Overwatch Epoch: {details.get('current_overwatch_epoch', 'N/A')}\n"
|
|
98
|
+
content += f" • Current Epoch: {details.get('current_epoch', 'N/A')}\n"
|
|
99
|
+
content += f" • Overwatch Nodes: {details.get('total_overwatch_nodes', 'N/A')}/{details.get('max_overwatch_nodes', 'N/A')}\n"
|
|
100
|
+
content += f" • Active Subnets: {details.get('total_active_subnets', 'N/A')}\n"
|
|
101
|
+
content += "\n"
|
|
102
|
+
|
|
103
|
+
# Show your status
|
|
104
|
+
content += "[htcli.accent]Your Status:[/htcli.accent]\n"
|
|
105
|
+
content += f" • Subnets with Nodes: {details.get('coldkey_subnets', 0)}\n"
|
|
106
|
+
content += f" • Balance: {details.get('balance_tensor', 0):,.2f}\n"
|
|
107
|
+
|
|
108
|
+
# Tips if not qualified
|
|
109
|
+
if not can_register:
|
|
110
|
+
content += "\n[htcli.warning]💡 Tips to Qualify:[/htcli.warning]\n"
|
|
111
|
+
content += " • Run nodes on multiple subnets to increase diversification\n"
|
|
112
|
+
content += " • Maintain high attestation rate to build reputation\n"
|
|
113
|
+
content += f" • Need at least {details.get('min_stake_tensor', 0):,.2f} for initial stake\n"
|
|
114
|
+
|
|
115
|
+
panel = HTCLIPanel(
|
|
116
|
+
content,
|
|
117
|
+
title=f"{title_emoji} Overwatch Qualification Check",
|
|
118
|
+
border_style=border_style,
|
|
119
|
+
highlight=True,
|
|
120
|
+
)
|
|
121
|
+
panel.render(console.console)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def display_overwatch_list(nodes_data: List[Any]):
|
|
125
|
+
"""Display all overwatch nodes from RPC response."""
|
|
126
|
+
if not nodes_data:
|
|
127
|
+
console.print(info("No overwatch nodes found on the network"))
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
# Create table for overwatch nodes
|
|
131
|
+
table = HTCLITable(title="Overwatch Nodes")
|
|
132
|
+
table.add_column("ID", style="htcli.accent", justify="right")
|
|
133
|
+
table.add_column("Coldkey", style="htcli.address")
|
|
134
|
+
table.add_column("Hotkey", style="htcli.address")
|
|
135
|
+
table.add_column("Stake", style="htcli.value", justify="right")
|
|
136
|
+
table.add_column("Penalties", style="htcli.value", justify="right")
|
|
137
|
+
|
|
138
|
+
for node in nodes_data:
|
|
139
|
+
node_id = getattr(node, "overwatch_node_id", "N/A")
|
|
140
|
+
coldkey = getattr(node, "coldkey", "N/A")
|
|
141
|
+
hotkey = getattr(node, "hotkey", "N/A")
|
|
142
|
+
penalties = getattr(node, "penalties", 0)
|
|
143
|
+
stake_balance = getattr(node, "stake_balance", 0)
|
|
144
|
+
stake_tensor = stake_balance / 1e18 if stake_balance else 0
|
|
145
|
+
|
|
146
|
+
# Truncate addresses for display: 0x<5 chars>...<5 chars>
|
|
147
|
+
from ...utils.wallet.crypto import format_address_display
|
|
148
|
+
coldkey_display = format_address_display(str(coldkey))
|
|
149
|
+
hotkey_display = format_address_display(str(hotkey))
|
|
150
|
+
|
|
151
|
+
table.add_row(
|
|
152
|
+
str(node_id),
|
|
153
|
+
coldkey_display,
|
|
154
|
+
hotkey_display,
|
|
155
|
+
f"{stake_tensor:,.2f}",
|
|
156
|
+
str(penalties),
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
table.render()
|
|
160
|
+
console.print(f"\n[htcli.info]Total: {len(nodes_data)} overwatch node(s)[/]")
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def display_overwatch_node_info(node_data: Any):
|
|
165
|
+
"""Display specific overwatch node information from RPC response."""
|
|
166
|
+
from ...utils.wallet.crypto import format_address_display
|
|
167
|
+
|
|
168
|
+
if not node_data:
|
|
169
|
+
console.print(info("Overwatch node not found"))
|
|
170
|
+
return
|
|
171
|
+
|
|
172
|
+
node_id = getattr(node_data, "overwatch_node_id", "N/A")
|
|
173
|
+
coldkey = getattr(node_data, "coldkey", "N/A")
|
|
174
|
+
hotkey = getattr(node_data, "hotkey", "N/A")
|
|
175
|
+
penalties = getattr(node_data, "penalties", 0)
|
|
176
|
+
peer_ids = getattr(node_data, "peer_ids", [])
|
|
177
|
+
reputation = getattr(node_data, "reputation", None)
|
|
178
|
+
stake_balance = getattr(node_data, "stake_balance", 0)
|
|
179
|
+
|
|
180
|
+
# Format stake balance
|
|
181
|
+
stake_tensor = stake_balance / 1e18 if stake_balance else 0
|
|
182
|
+
|
|
183
|
+
# Format peer IDs
|
|
184
|
+
peer_ids_display = "None"
|
|
185
|
+
if peer_ids:
|
|
186
|
+
formatted_peers = []
|
|
187
|
+
# Sort by subnet ID for consistent display
|
|
188
|
+
sorted_peers = sorted(peer_ids.items())
|
|
189
|
+
|
|
190
|
+
for subnet_id, peer_id in sorted_peers[:5]: # Limit to first 5
|
|
191
|
+
formatted_peers.append(f" • Subnet {subnet_id}: {format_peer_id(peer_id)}")
|
|
192
|
+
peer_ids_display = "\n".join(formatted_peers)
|
|
193
|
+
if len(peer_ids) > 5:
|
|
194
|
+
peer_ids_display += f"\n ... and {len(peer_ids) - 5} more"
|
|
195
|
+
|
|
196
|
+
# Format reputation
|
|
197
|
+
reputation_display = "[dim]No reputation data[/dim]"
|
|
198
|
+
if reputation:
|
|
199
|
+
rep_score = reputation.get('score', 0) / 1e12
|
|
200
|
+
reputation_display = f""" • Score: {rep_score:,.2f}
|
|
201
|
+
• Lifetime Nodes: {reputation.get('lifetime_node_count', 0)}
|
|
202
|
+
• Active Nodes: {reputation.get('total_active_nodes', 0)}
|
|
203
|
+
• Avg Attestation: {reputation.get('average_attestation', 0)}"""
|
|
204
|
+
|
|
205
|
+
content = f"""[htcli.accent]Basic Information[/htcli.accent]
|
|
206
|
+
[htcli.value]Overwatch Node ID:[/htcli.value] {node_id}
|
|
207
|
+
[htcli.value]Penalties:[/htcli.value] {penalties}
|
|
208
|
+
|
|
209
|
+
[htcli.accent]Stake[/htcli.accent]
|
|
210
|
+
[htcli.value]Current Stake:[/htcli.value] {stake_tensor:,.4f}
|
|
211
|
+
|
|
212
|
+
[htcli.accent]Wallet Addresses[/htcli.accent]
|
|
213
|
+
[htcli.value]Coldkey:[/htcli.value] [htcli.address]{format_address_display(str(coldkey), truncate=False)}[/htcli.address]
|
|
214
|
+
[htcli.value]Hotkey:[/htcli.value] [htcli.address]{format_address_display(str(hotkey), truncate=False)}[/htcli.address]
|
|
215
|
+
|
|
216
|
+
[htcli.accent]Peer IDs by Subnet[/htcli.accent]
|
|
217
|
+
{peer_ids_display}
|
|
218
|
+
|
|
219
|
+
[htcli.accent]Reputation[/htcli.accent]
|
|
220
|
+
{reputation_display}
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
panel = HTCLIPanel(
|
|
224
|
+
content,
|
|
225
|
+
title=f"🔮 Overwatch Node {node_id} Details",
|
|
226
|
+
border_style="htcli.info",
|
|
227
|
+
highlight=True,
|
|
228
|
+
)
|
|
229
|
+
panel.render(console.console)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
# ============================================================================
|
|
233
|
+
# EXTRINSIC DISPLAY FUNCTIONS - Write Operations
|
|
234
|
+
# ============================================================================
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def display_overwatch_register_result(response: dict):
|
|
238
|
+
"""Display overwatch node registration result."""
|
|
239
|
+
from ...utils.wallet.crypto import format_address_display
|
|
240
|
+
|
|
241
|
+
if response.get("success"):
|
|
242
|
+
stake_wei = response.get("stake_added", 0)
|
|
243
|
+
stake_tensor = stake_wei / 1e18 if stake_wei else 0
|
|
244
|
+
hotkey = format_address_display(response.get("hotkey", "N/A"), truncate=False)
|
|
245
|
+
|
|
246
|
+
content = f"""[htcli.success]✅ Overwatch Node Registered Successfully![/htcli.success]
|
|
247
|
+
|
|
248
|
+
[htcli.value]Overwatch Node ID:[/htcli.value] {response.get("overwatch_node_id", "Pending")}
|
|
249
|
+
[htcli.value]Hotkey:[/htcli.value] [htcli.address]{hotkey}[/htcli.address]
|
|
250
|
+
[htcli.value]Initial Stake:[/htcli.value] {stake_tensor:,.4f}
|
|
251
|
+
[htcli.value]Transaction Hash:[/htcli.value] {response.get("transaction_hash", "N/A")}
|
|
252
|
+
[htcli.value]Block Number:[/htcli.value] {response.get("block_number", "N/A")}
|
|
253
|
+
"""
|
|
254
|
+
if response.get("block_hash"):
|
|
255
|
+
content += f"[htcli.value]Block Hash:[/htcli.value] {response.get('block_hash')}\n"
|
|
256
|
+
content += "\n[htcli.info]💡 Your overwatch node is now registered! You can start participating in consensus.[/htcli.info]\n"
|
|
257
|
+
|
|
258
|
+
panel = HTCLIPanel(
|
|
259
|
+
content,
|
|
260
|
+
title="🔮 Overwatch Node Registration Complete",
|
|
261
|
+
border_style="htcli.success",
|
|
262
|
+
highlight=True,
|
|
263
|
+
)
|
|
264
|
+
panel.render(console.console)
|
|
265
|
+
else:
|
|
266
|
+
error_msg = response.get("error", "Unknown error")
|
|
267
|
+
panel = HTCLIPanel(
|
|
268
|
+
f"""{error_msg}
|
|
269
|
+
|
|
270
|
+
💡 Troubleshooting:
|
|
271
|
+
• Ensure you meet the reputation requirements for overwatch nodes
|
|
272
|
+
• Verify you have sufficient balance for stake and fees
|
|
273
|
+
• Check that your hotkey is not already registered
|
|
274
|
+
• Review: htcli overwatch register --help
|
|
275
|
+
""",
|
|
276
|
+
title="⚠️ Overwatch Node Registration Failed",
|
|
277
|
+
border_style="htcli.error",
|
|
278
|
+
highlight=True,
|
|
279
|
+
)
|
|
280
|
+
panel.render(console.console)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def display_overwatch_remove_result(response: dict):
|
|
284
|
+
"""Display overwatch node removal result."""
|
|
285
|
+
if response.get("success"):
|
|
286
|
+
content = f"""[htcli.success]✅ Overwatch Node Removed Successfully[/htcli.success]
|
|
287
|
+
|
|
288
|
+
[htcli.value]Overwatch Node ID:[/htcli.value] {response.get("overwatch_node_id", "N/A")}
|
|
289
|
+
[htcli.value]Transaction Hash:[/htcli.value] {response.get("transaction_hash", "N/A")}
|
|
290
|
+
[htcli.value]Block Number:[/htcli.value] {response.get("block_number", "N/A")}
|
|
291
|
+
"""
|
|
292
|
+
if response.get("block_hash"):
|
|
293
|
+
content += f"[htcli.value]Block Hash:[/htcli.value] {response.get('block_hash')}\n"
|
|
294
|
+
content += "\n[htcli.info]💡 Your overwatch node has been removed. Any staked balance will enter unbonding.[/htcli.info]\n"
|
|
295
|
+
|
|
296
|
+
panel = HTCLIPanel(
|
|
297
|
+
content,
|
|
298
|
+
title="🗑️ Overwatch Node Removal Complete",
|
|
299
|
+
border_style="htcli.success",
|
|
300
|
+
highlight=True,
|
|
301
|
+
)
|
|
302
|
+
panel.render(console.console)
|
|
303
|
+
else:
|
|
304
|
+
error_msg = response.get("error", "Unknown error")
|
|
305
|
+
console.print(f"[htcli.error]❌ Failed to remove overwatch node: {error_msg}[/]")
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def display_overwatch_peer_id_result(response: dict):
|
|
309
|
+
"""Display set peer ID result."""
|
|
310
|
+
if response.get("success"):
|
|
311
|
+
content = f"""[htcli.success]✅ Peer ID Set Successfully[/htcli.success]
|
|
312
|
+
|
|
313
|
+
[htcli.value]Subnet ID:[/htcli.value] {response.get("subnet_id", "N/A")}
|
|
314
|
+
[htcli.value]Overwatch Node ID:[/htcli.value] {response.get("overwatch_node_id", "N/A")}
|
|
315
|
+
[htcli.value]Peer ID:[/htcli.value] {response.get("peer_id", "N/A")}
|
|
316
|
+
[htcli.value]Transaction Hash:[/htcli.value] {response.get("transaction_hash", "N/A")}
|
|
317
|
+
[htcli.value]Block Number:[/htcli.value] {response.get("block_number", "N/A")}
|
|
318
|
+
"""
|
|
319
|
+
if response.get("block_hash"):
|
|
320
|
+
content += f"[htcli.value]Block Hash:[/htcli.value] {response.get('block_hash')}\n"
|
|
321
|
+
content += "\n[htcli.info]💡 Your peer ID has been registered for this subnet.[/htcli.info]\n"
|
|
322
|
+
|
|
323
|
+
panel = HTCLIPanel(
|
|
324
|
+
content,
|
|
325
|
+
title="🔗 Peer ID Update Complete",
|
|
326
|
+
border_style="htcli.success",
|
|
327
|
+
highlight=True,
|
|
328
|
+
)
|
|
329
|
+
panel.render(console.console)
|
|
330
|
+
else:
|
|
331
|
+
error_msg = response.get("error", "Unknown error")
|
|
332
|
+
console.print(f"[htcli.error]❌ Failed to set peer ID: {error_msg}[/]")
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def display_overwatch_stake_add_result(response: dict):
|
|
336
|
+
"""Display add stake result."""
|
|
337
|
+
from ...utils.wallet.crypto import format_address_display
|
|
338
|
+
|
|
339
|
+
if response.get("success"):
|
|
340
|
+
stake_wei = response.get("stake_added", 0)
|
|
341
|
+
stake_tensor = stake_wei / 1e18 if stake_wei else 0
|
|
342
|
+
hotkey = format_address_display(response.get("hotkey", "N/A"), truncate=False)
|
|
343
|
+
|
|
344
|
+
content = f"""[htcli.success]✅ Stake Added Successfully[/htcli.success]
|
|
345
|
+
|
|
346
|
+
[htcli.value]Overwatch Node ID:[/htcli.value] {response.get("overwatch_node_id", "N/A")}
|
|
347
|
+
[htcli.value]Hotkey:[/htcli.value] [htcli.address]{hotkey}[/htcli.address]
|
|
348
|
+
[htcli.value]Stake Added:[/htcli.value] {stake_tensor:,.4f}
|
|
349
|
+
[htcli.value]Transaction Hash:[/htcli.value] {response.get("transaction_hash", "N/A")}
|
|
350
|
+
[htcli.value]Block Number:[/htcli.value] {response.get("block_number", "N/A")}
|
|
351
|
+
"""
|
|
352
|
+
if response.get("block_hash"):
|
|
353
|
+
content += f"[htcli.value]Block Hash:[/htcli.value] {response.get('block_hash')}\n"
|
|
354
|
+
|
|
355
|
+
panel = HTCLIPanel(
|
|
356
|
+
content,
|
|
357
|
+
title="💰 Overwatch Stake Added",
|
|
358
|
+
border_style="htcli.success",
|
|
359
|
+
highlight=True,
|
|
360
|
+
)
|
|
361
|
+
panel.render(console.console)
|
|
362
|
+
else:
|
|
363
|
+
error_msg = response.get("error", "Unknown error")
|
|
364
|
+
console.print(f"[htcli.error]❌ Failed to add stake: {error_msg}[/]")
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def display_overwatch_stake_remove_result(response: dict):
|
|
368
|
+
"""Display remove stake result."""
|
|
369
|
+
from ...utils.wallet.crypto import format_address_display
|
|
370
|
+
|
|
371
|
+
if response.get("success"):
|
|
372
|
+
stake_wei = response.get("stake_removed", 0)
|
|
373
|
+
stake_tensor = stake_wei / 1e18 if stake_wei else 0
|
|
374
|
+
hotkey = format_address_display(response.get("hotkey", "N/A"), truncate=False)
|
|
375
|
+
|
|
376
|
+
content = f"""[htcli.success]✅ Stake Removal Started[/htcli.success]
|
|
377
|
+
|
|
378
|
+
[htcli.value]Hotkey:[/htcli.value] [htcli.address]{hotkey}[/htcli.address]
|
|
379
|
+
[htcli.value]Stake Removed:[/htcli.value] {stake_tensor:,.4f}
|
|
380
|
+
[htcli.value]Transaction Hash:[/htcli.value] {response.get("transaction_hash", "N/A")}
|
|
381
|
+
[htcli.value]Block Number:[/htcli.value] {response.get("block_number", "N/A")}
|
|
382
|
+
"""
|
|
383
|
+
if response.get("block_hash"):
|
|
384
|
+
content += f"[htcli.value]Block Hash:[/htcli.value] {response.get('block_hash')}\n"
|
|
385
|
+
content += "\n[htcli.warning]⚠️ Removed stake will be available after the unbonding period.[/htcli.warning]\n"
|
|
386
|
+
|
|
387
|
+
panel = HTCLIPanel(
|
|
388
|
+
content,
|
|
389
|
+
title="💸 Overwatch Stake Removal Initiated",
|
|
390
|
+
border_style="htcli.success",
|
|
391
|
+
highlight=True,
|
|
392
|
+
)
|
|
393
|
+
panel.render(console.console)
|
|
394
|
+
else:
|
|
395
|
+
error_msg = response.get("error", "Unknown error")
|
|
396
|
+
console.print(f"[htcli.error]❌ Failed to remove stake: {error_msg}[/]")
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Error handling utilities for overwatch commands.
|
|
3
|
+
Provides user-friendly error messages for all overwatch-related errors.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from ...ui.components import HTCLIPanel
|
|
9
|
+
from ...ui.display import HTCLIConsole
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def handle_overwatch_error(
|
|
13
|
+
error_msg: str,
|
|
14
|
+
overwatch_node_id: Optional[int] = None,
|
|
15
|
+
operation: str = "operation",
|
|
16
|
+
client=None,
|
|
17
|
+
) -> bool:
|
|
18
|
+
"""
|
|
19
|
+
Handle overwatch-related errors with user-friendly messages.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
error_msg: The error message from the blockchain
|
|
23
|
+
overwatch_node_id: The overwatch node ID (if applicable)
|
|
24
|
+
operation: Description of the operation being performed
|
|
25
|
+
client: Optional client instance for fetching additional info
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
bool: True if error was handled, False otherwise
|
|
29
|
+
"""
|
|
30
|
+
console = HTCLIConsole()
|
|
31
|
+
node_id_str = f" {overwatch_node_id}" if overwatch_node_id is not None else ""
|
|
32
|
+
|
|
33
|
+
# Normalize error message for case-insensitive matching
|
|
34
|
+
error_msg_lower = error_msg.lower()
|
|
35
|
+
|
|
36
|
+
# =========================================================================
|
|
37
|
+
# Registration Errors
|
|
38
|
+
# =========================================================================
|
|
39
|
+
|
|
40
|
+
if "coldkeyblacklisted" in error_msg_lower:
|
|
41
|
+
error_panel = HTCLIPanel(
|
|
42
|
+
f"Your coldkey is blacklisted from registering overwatch nodes.\n\n"
|
|
43
|
+
f"💡 Blacklisted accounts cannot participate as overwatch nodes.\n"
|
|
44
|
+
f" Contact support if you believe this is in error.",
|
|
45
|
+
title="🚫 Coldkey Blacklisted",
|
|
46
|
+
border_style="htcli.error",
|
|
47
|
+
highlight=True,
|
|
48
|
+
)
|
|
49
|
+
error_panel.render(console.console)
|
|
50
|
+
return True
|
|
51
|
+
|
|
52
|
+
elif "overwatchepochiszero" in error_msg_lower:
|
|
53
|
+
error_panel = HTCLIPanel(
|
|
54
|
+
f"Overwatch epoch has not started yet.\n\n"
|
|
55
|
+
f"💡 The overwatch system is not yet active on the network.\n"
|
|
56
|
+
f" Wait for the first overwatch epoch to begin before registering.",
|
|
57
|
+
title="⏳ Overwatch Not Started",
|
|
58
|
+
border_style="htcli.warning",
|
|
59
|
+
highlight=True,
|
|
60
|
+
)
|
|
61
|
+
error_panel.render(console.console)
|
|
62
|
+
return True
|
|
63
|
+
|
|
64
|
+
elif "maxoverwatchnodes" in error_msg_lower:
|
|
65
|
+
error_panel = HTCLIPanel(
|
|
66
|
+
f"Maximum number of overwatch nodes has been reached.\n\n"
|
|
67
|
+
f"💡 The network has a cap on the number of overwatch nodes.\n"
|
|
68
|
+
f" Wait for a slot to become available when another node exits.",
|
|
69
|
+
title="❌ Max Overwatch Nodes Reached",
|
|
70
|
+
border_style="htcli.error",
|
|
71
|
+
highlight=True,
|
|
72
|
+
)
|
|
73
|
+
error_panel.render(console.console)
|
|
74
|
+
return True
|
|
75
|
+
|
|
76
|
+
elif "coldkeymatcheshotkey" in error_msg_lower:
|
|
77
|
+
error_panel = HTCLIPanel(
|
|
78
|
+
f"Coldkey and hotkey cannot be the same account.\n\n"
|
|
79
|
+
f"💡 You must use different accounts for coldkey and hotkey.\n"
|
|
80
|
+
f" Generate a separate hotkey: htcli wallet generate-hotkey",
|
|
81
|
+
title="❌ Invalid Key Configuration",
|
|
82
|
+
border_style="htcli.error",
|
|
83
|
+
highlight=True,
|
|
84
|
+
)
|
|
85
|
+
error_panel.render(console.console)
|
|
86
|
+
return True
|
|
87
|
+
|
|
88
|
+
elif "hotkeyhasowner" in error_msg_lower:
|
|
89
|
+
error_panel = HTCLIPanel(
|
|
90
|
+
f"This hotkey is already registered to another account.\n\n"
|
|
91
|
+
f"💡 Each hotkey can only be owned by one coldkey.\n"
|
|
92
|
+
f" Use a fresh hotkey: htcli wallet generate-hotkey",
|
|
93
|
+
title="❌ Hotkey Already Owned",
|
|
94
|
+
border_style="htcli.error",
|
|
95
|
+
highlight=True,
|
|
96
|
+
)
|
|
97
|
+
error_panel.render(console.console)
|
|
98
|
+
return True
|
|
99
|
+
|
|
100
|
+
elif "hotkeyalreadyregisteredtocoldkey" in error_msg_lower:
|
|
101
|
+
error_panel = HTCLIPanel(
|
|
102
|
+
f"This hotkey is already registered to your coldkey.\n\n"
|
|
103
|
+
f"💡 You've already registered this hotkey.\n"
|
|
104
|
+
f" Check your existing nodes: htcli overwatch list",
|
|
105
|
+
title="❌ Hotkey Already Registered",
|
|
106
|
+
border_style="htcli.error",
|
|
107
|
+
highlight=True,
|
|
108
|
+
)
|
|
109
|
+
error_panel.render(console.console)
|
|
110
|
+
return True
|
|
111
|
+
|
|
112
|
+
elif "coldkeynotoverwatchqualified" in error_msg_lower:
|
|
113
|
+
error_panel = HTCLIPanel(
|
|
114
|
+
f"Your coldkey is not qualified to run an overwatch node.\n\n"
|
|
115
|
+
f"💡 Overwatch nodes require sufficient reputation.\n"
|
|
116
|
+
f" Build reputation by running subnet nodes successfully.\n"
|
|
117
|
+
f" Check your reputation status: htcli wallet info",
|
|
118
|
+
title="❌ Not Qualified",
|
|
119
|
+
border_style="htcli.error",
|
|
120
|
+
highlight=True,
|
|
121
|
+
)
|
|
122
|
+
error_panel.render(console.console)
|
|
123
|
+
return True
|
|
124
|
+
|
|
125
|
+
# =========================================================================
|
|
126
|
+
# Node Existence Errors
|
|
127
|
+
# =========================================================================
|
|
128
|
+
|
|
129
|
+
elif "invalidoverwatchnodeid" in error_msg_lower or "overwatchnodenotexist" in error_msg_lower:
|
|
130
|
+
error_panel = HTCLIPanel(
|
|
131
|
+
f"Overwatch node{node_id_str} does not exist.\n\n"
|
|
132
|
+
f"💡 Check the overwatch node ID and try again.\n"
|
|
133
|
+
f" Use: htcli overwatch list to see all overwatch nodes",
|
|
134
|
+
title="❌ Invalid Overwatch Node",
|
|
135
|
+
border_style="htcli.error",
|
|
136
|
+
highlight=True,
|
|
137
|
+
)
|
|
138
|
+
error_panel.render(console.console)
|
|
139
|
+
return True
|
|
140
|
+
|
|
141
|
+
elif "notoverwatchnodeowner" in error_msg_lower:
|
|
142
|
+
error_panel = HTCLIPanel(
|
|
143
|
+
f"You are not the owner of overwatch node{node_id_str}.\n\n"
|
|
144
|
+
f"💡 Only the coldkey owner can manage this overwatch node.\n"
|
|
145
|
+
f" Check your wallet: htcli wallet list",
|
|
146
|
+
title="❌ Permission Denied",
|
|
147
|
+
border_style="htcli.error",
|
|
148
|
+
highlight=True,
|
|
149
|
+
)
|
|
150
|
+
error_panel.render(console.console)
|
|
151
|
+
return True
|
|
152
|
+
|
|
153
|
+
# =========================================================================
|
|
154
|
+
# Stake Errors
|
|
155
|
+
# =========================================================================
|
|
156
|
+
|
|
157
|
+
elif "overwatchminstakenotreached" in error_msg_lower or "minoverwatchstake" in error_msg_lower:
|
|
158
|
+
error_panel = HTCLIPanel(
|
|
159
|
+
f"Stake amount is below the minimum requirement for overwatch nodes.\n\n"
|
|
160
|
+
f"💡 Overwatch nodes require a minimum stake to participate.\n"
|
|
161
|
+
f" Increase your stake amount and try again.",
|
|
162
|
+
title="❌ Below Minimum Stake",
|
|
163
|
+
border_style="htcli.error",
|
|
164
|
+
highlight=True,
|
|
165
|
+
)
|
|
166
|
+
error_panel.render(console.console)
|
|
167
|
+
return True
|
|
168
|
+
|
|
169
|
+
elif "notenoughoverwatchstake" in error_msg_lower:
|
|
170
|
+
error_panel = HTCLIPanel(
|
|
171
|
+
f"Insufficient overwatch stake to remove.\n\n"
|
|
172
|
+
f"💡 You don't have enough staked balance to remove this amount.\n"
|
|
173
|
+
f" Check your current stake: htcli overwatch info",
|
|
174
|
+
title="❌ Insufficient Stake",
|
|
175
|
+
border_style="htcli.error",
|
|
176
|
+
highlight=True,
|
|
177
|
+
)
|
|
178
|
+
error_panel.render(console.console)
|
|
179
|
+
return True
|
|
180
|
+
|
|
181
|
+
elif "notenoughbalancetostake" in error_msg_lower or "insufficientbalance" in error_msg_lower:
|
|
182
|
+
error_panel = HTCLIPanel(
|
|
183
|
+
f"Insufficient balance to add stake.\n\n"
|
|
184
|
+
f"💡 You don't have enough balance in your wallet.\n"
|
|
185
|
+
f" Check your balance: htcli wallet balance",
|
|
186
|
+
title="❌ Insufficient Balance",
|
|
187
|
+
border_style="htcli.error",
|
|
188
|
+
highlight=True,
|
|
189
|
+
)
|
|
190
|
+
error_panel.render(console.console)
|
|
191
|
+
return True
|
|
192
|
+
|
|
193
|
+
# =========================================================================
|
|
194
|
+
# Peer ID Errors
|
|
195
|
+
# =========================================================================
|
|
196
|
+
|
|
197
|
+
elif "invalidpeerid" in error_msg_lower:
|
|
198
|
+
error_panel = HTCLIPanel(
|
|
199
|
+
f"Invalid peer ID format.\n\n"
|
|
200
|
+
f"💡 The peer ID must be a valid libp2p peer identifier.\n"
|
|
201
|
+
f" Example: 12D3KooWxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
|
202
|
+
title="❌ Invalid Peer ID",
|
|
203
|
+
border_style="htcli.error",
|
|
204
|
+
highlight=True,
|
|
205
|
+
)
|
|
206
|
+
error_panel.render(console.console)
|
|
207
|
+
return True
|
|
208
|
+
|
|
209
|
+
elif "peeridalreadyinuse" in error_msg_lower:
|
|
210
|
+
error_panel = HTCLIPanel(
|
|
211
|
+
f"This peer ID is already in use by another node.\n\n"
|
|
212
|
+
f"💡 Each node must have a unique peer ID.\n"
|
|
213
|
+
f" Generate a new peer ID for your node.",
|
|
214
|
+
title="❌ Peer ID Already Used",
|
|
215
|
+
border_style="htcli.error",
|
|
216
|
+
highlight=True,
|
|
217
|
+
)
|
|
218
|
+
error_panel.render(console.console)
|
|
219
|
+
return True
|
|
220
|
+
|
|
221
|
+
# =========================================================================
|
|
222
|
+
# Removal Errors
|
|
223
|
+
# =========================================================================
|
|
224
|
+
|
|
225
|
+
elif "overwatchnodecannotberemoved" in error_msg_lower:
|
|
226
|
+
error_panel = HTCLIPanel(
|
|
227
|
+
f"Overwatch node{node_id_str} cannot be removed at this time.\n\n"
|
|
228
|
+
f"💡 The node may be in an active validation epoch.\n"
|
|
229
|
+
f" Wait for the current epoch to complete before removing.",
|
|
230
|
+
title="⚠️ Cannot Remove Node",
|
|
231
|
+
border_style="htcli.warning",
|
|
232
|
+
highlight=True,
|
|
233
|
+
)
|
|
234
|
+
error_panel.render(console.console)
|
|
235
|
+
return True
|
|
236
|
+
|
|
237
|
+
elif "overwatchnodealreadyremoved" in error_msg_lower:
|
|
238
|
+
error_panel = HTCLIPanel(
|
|
239
|
+
f"Overwatch node{node_id_str} has already been removed.\n\n"
|
|
240
|
+
f"💡 This node no longer exists in the network.",
|
|
241
|
+
title="⚠️ Node Already Removed",
|
|
242
|
+
border_style="htcli.warning",
|
|
243
|
+
highlight=True,
|
|
244
|
+
)
|
|
245
|
+
error_panel.render(console.console)
|
|
246
|
+
return True
|
|
247
|
+
|
|
248
|
+
# =========================================================================
|
|
249
|
+
# System Errors
|
|
250
|
+
# =========================================================================
|
|
251
|
+
|
|
252
|
+
elif "txratelimitexceeded" in error_msg_lower:
|
|
253
|
+
error_panel = HTCLIPanel(
|
|
254
|
+
f"Transaction rate limit exceeded.\n\n"
|
|
255
|
+
f"⏱️ You've exceeded the maximum transaction rate.\n\n"
|
|
256
|
+
f"💡 Wait a moment before submitting another transaction.",
|
|
257
|
+
title="⏳ Rate Limit Exceeded",
|
|
258
|
+
border_style="htcli.warning",
|
|
259
|
+
highlight=True,
|
|
260
|
+
)
|
|
261
|
+
error_panel.render(console.console)
|
|
262
|
+
return True
|
|
263
|
+
|
|
264
|
+
elif "badorigin" in error_msg_lower:
|
|
265
|
+
error_panel = HTCLIPanel(
|
|
266
|
+
f"Invalid transaction signature.\n\n"
|
|
267
|
+
f"💡 Make sure you're using the correct wallet.\n"
|
|
268
|
+
f" Check your wallet: htcli wallet list",
|
|
269
|
+
title="❌ Invalid Signature",
|
|
270
|
+
border_style="htcli.error",
|
|
271
|
+
highlight=True,
|
|
272
|
+
)
|
|
273
|
+
error_panel.render(console.console)
|
|
274
|
+
return True
|
|
275
|
+
|
|
276
|
+
return False # Error not handled by this function
|