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.
Files changed (140) hide show
  1. htcli-1.1.0.dist-info/METADATA +509 -0
  2. htcli-1.1.0.dist-info/RECORD +140 -0
  3. htcli-1.1.0.dist-info/WHEEL +4 -0
  4. htcli-1.1.0.dist-info/entry_points.txt +2 -0
  5. htcli-1.1.0.dist-info/licenses/LICENSE +21 -0
  6. src/__init__.py +0 -0
  7. src/htcli/__init__.py +5 -0
  8. src/htcli/client/__init__.py +338 -0
  9. src/htcli/client/extrinsics/__init__.py +26 -0
  10. src/htcli/client/extrinsics/base.py +487 -0
  11. src/htcli/client/extrinsics/consensus.py +79 -0
  12. src/htcli/client/extrinsics/governance.py +714 -0
  13. src/htcli/client/extrinsics/identity.py +490 -0
  14. src/htcli/client/extrinsics/node.py +1054 -0
  15. src/htcli/client/extrinsics/overwatch.py +401 -0
  16. src/htcli/client/extrinsics/staking.py +1504 -0
  17. src/htcli/client/extrinsics/subnet.py +2218 -0
  18. src/htcli/client/extrinsics/validator.py +203 -0
  19. src/htcli/client/extrinsics/wallet.py +323 -0
  20. src/htcli/client/offchain/__init__.py +10 -0
  21. src/htcli/client/offchain/backup.py +385 -0
  22. src/htcli/client/offchain/config.py +541 -0
  23. src/htcli/client/offchain/wallet.py +839 -0
  24. src/htcli/client/rpc/__init__.py +20 -0
  25. src/htcli/client/rpc/chain.py +568 -0
  26. src/htcli/client/rpc/node.py +783 -0
  27. src/htcli/client/rpc/overwatch.py +680 -0
  28. src/htcli/client/rpc/staking.py +216 -0
  29. src/htcli/client/rpc/subnet.py +2104 -0
  30. src/htcli/client/rpc/wallet.py +912 -0
  31. src/htcli/commands/__init__.py +31 -0
  32. src/htcli/commands/chain/__init__.py +66 -0
  33. src/htcli/commands/chain/display.py +204 -0
  34. src/htcli/commands/chain/handlers.py +260 -0
  35. src/htcli/commands/config/__init__.py +158 -0
  36. src/htcli/commands/config/display.py +353 -0
  37. src/htcli/commands/config/handlers.py +347 -0
  38. src/htcli/commands/config/prompts.py +357 -0
  39. src/htcli/commands/consensus/__init__.py +61 -0
  40. src/htcli/commands/consensus/handlers.py +100 -0
  41. src/htcli/commands/governance/__init__.py +49 -0
  42. src/htcli/commands/governance/handlers.py +81 -0
  43. src/htcli/commands/node/__init__.py +304 -0
  44. src/htcli/commands/node/display.py +749 -0
  45. src/htcli/commands/node/error_handling.py +470 -0
  46. src/htcli/commands/node/handlers.py +844 -0
  47. src/htcli/commands/node/prompts.py +346 -0
  48. src/htcli/commands/overwatch/__init__.py +219 -0
  49. src/htcli/commands/overwatch/display.py +396 -0
  50. src/htcli/commands/overwatch/error_handling.py +276 -0
  51. src/htcli/commands/overwatch/handlers.py +443 -0
  52. src/htcli/commands/overwatch/prompts.py +359 -0
  53. src/htcli/commands/stake/__init__.py +736 -0
  54. src/htcli/commands/stake/display.py +1103 -0
  55. src/htcli/commands/stake/error_handling.py +425 -0
  56. src/htcli/commands/stake/handlers.py +1902 -0
  57. src/htcli/commands/stake/prompts.py +1080 -0
  58. src/htcli/commands/subnet/__init__.py +639 -0
  59. src/htcli/commands/subnet/display.py +801 -0
  60. src/htcli/commands/subnet/error_handling.py +524 -0
  61. src/htcli/commands/subnet/handlers.py +2855 -0
  62. src/htcli/commands/subnet/prompts.py +1225 -0
  63. src/htcli/commands/validator/__init__.py +192 -0
  64. src/htcli/commands/validator/display.py +54 -0
  65. src/htcli/commands/validator/handlers.py +340 -0
  66. src/htcli/commands/wallet/__init__.py +546 -0
  67. src/htcli/commands/wallet/display.py +806 -0
  68. src/htcli/commands/wallet/error_handling.py +210 -0
  69. src/htcli/commands/wallet/handlers.py +3040 -0
  70. src/htcli/commands/wallet/prompts.py +1518 -0
  71. src/htcli/config.py +184 -0
  72. src/htcli/dependencies.py +186 -0
  73. src/htcli/errors/__init__.py +63 -0
  74. src/htcli/errors/base.py +141 -0
  75. src/htcli/errors/display.py +20 -0
  76. src/htcli/errors/handlers.py +710 -0
  77. src/htcli/main.py +343 -0
  78. src/htcli/models/__init__.py +21 -0
  79. src/htcli/models/enums/enum_types.py +35 -0
  80. src/htcli/models/errors.py +103 -0
  81. src/htcli/models/requests/__init__.py +197 -0
  82. src/htcli/models/requests/config.py +70 -0
  83. src/htcli/models/requests/consensus.py +19 -0
  84. src/htcli/models/requests/governance.py +38 -0
  85. src/htcli/models/requests/identity.py +51 -0
  86. src/htcli/models/requests/key.py +22 -0
  87. src/htcli/models/requests/node.py +91 -0
  88. src/htcli/models/requests/overwatch.py +64 -0
  89. src/htcli/models/requests/staking.py +580 -0
  90. src/htcli/models/requests/subnet.py +195 -0
  91. src/htcli/models/requests/validator.py +139 -0
  92. src/htcli/models/requests/wallet.py +118 -0
  93. src/htcli/models/responses/__init__.py +147 -0
  94. src/htcli/models/responses/base.py +18 -0
  95. src/htcli/models/responses/chain.py +39 -0
  96. src/htcli/models/responses/config.py +58 -0
  97. src/htcli/models/responses/identity.py +102 -0
  98. src/htcli/models/responses/overwatch.py +51 -0
  99. src/htcli/models/responses/staking.py +502 -0
  100. src/htcli/models/responses/subnet.py +856 -0
  101. src/htcli/models/responses/wallet.py +185 -0
  102. src/htcli/ui/__init__.py +87 -0
  103. src/htcli/ui/colors.py +309 -0
  104. src/htcli/ui/components/__init__.py +60 -0
  105. src/htcli/ui/components/panels.py +174 -0
  106. src/htcli/ui/components/progress.py +166 -0
  107. src/htcli/ui/components/spinners.py +92 -0
  108. src/htcli/ui/components/tables.py +809 -0
  109. src/htcli/ui/components/trees.py +721 -0
  110. src/htcli/ui/display.py +336 -0
  111. src/htcli/ui/prompts.py +870 -0
  112. src/htcli/utils/__init__.py +76 -0
  113. src/htcli/utils/blockchain/__init__.py +75 -0
  114. src/htcli/utils/blockchain/formatting.py +368 -0
  115. src/htcli/utils/blockchain/patches.py +286 -0
  116. src/htcli/utils/blockchain/peer_id.py +186 -0
  117. src/htcli/utils/blockchain/staking.py +448 -0
  118. src/htcli/utils/blockchain/type_registry.py +1373 -0
  119. src/htcli/utils/blockchain/validation.py +179 -0
  120. src/htcli/utils/cache.py +613 -0
  121. src/htcli/utils/constants.py +38 -0
  122. src/htcli/utils/legacy/__init__.py +12 -0
  123. src/htcli/utils/legacy/colors.py +311 -0
  124. src/htcli/utils/legacy/crypto.py +1176 -0
  125. src/htcli/utils/legacy/formatting.py +452 -0
  126. src/htcli/utils/legacy/interactive.py +306 -0
  127. src/htcli/utils/legacy/subnet_manifest.py +265 -0
  128. src/htcli/utils/legacy/validation.py +488 -0
  129. src/htcli/utils/logging.py +183 -0
  130. src/htcli/utils/network/__init__.py +20 -0
  131. src/htcli/utils/network/subnet.py +344 -0
  132. src/htcli/utils/prompts.py +27 -0
  133. src/htcli/utils/scale_codec.py +155 -0
  134. src/htcli/utils/validation/__init__.py +57 -0
  135. src/htcli/utils/validation/prompt_validators.py +267 -0
  136. src/htcli/utils/wallet/__init__.py +65 -0
  137. src/htcli/utils/wallet/auth.py +151 -0
  138. src/htcli/utils/wallet/core.py +1069 -0
  139. src/htcli/utils/wallet/crypto.py +1615 -0
  140. src/htcli/utils/wallet/migration.py +159 -0
src/htcli/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ """
2
+ Hypertensor CLI - A command-line interface for the Hypertensor blockchain.
3
+ """
4
+
5
+ __version__ = "1.1.0"
@@ -0,0 +1,338 @@
1
+ """
2
+ Clean client structure for Hypertensor CLI using 3-layer architecture.
3
+ The client only manages connections and exposes the 3 layers directly.
4
+ """
5
+
6
+ from pathlib import Path
7
+ from typing import Optional
8
+
9
+ from substrateinterface import SubstrateInterface
10
+
11
+ # Import logging
12
+ from ..utils.logging import get_logger
13
+ from .extrinsics import (
14
+ ConsensusExtrinsics,
15
+ GovernanceExtrinsics,
16
+ IdentityExtrinsics,
17
+ NodeExtrinsics,
18
+ OverwatchExtrinsics,
19
+ StakingExtrinsics,
20
+ SubnetExtrinsics,
21
+ ValidatorExtrinsics,
22
+ WalletExtrinsics,
23
+ )
24
+ from .offchain import BackupManager, ConfigManager, WalletManager
25
+ from .rpc import (
26
+ ChainRpcClient,
27
+ OverwatchRpcClient,
28
+ StakingRpcClient,
29
+ SubnetNodeRpcClient,
30
+ SubnetRpcClient,
31
+ WalletRpcClient,
32
+ )
33
+
34
+ logger = get_logger(__name__)
35
+
36
+
37
+ class RPCLayer:
38
+ """Container for RPC (read-only) operations."""
39
+
40
+ def __init__(self, substrate):
41
+ self.chain = ChainRpcClient(substrate)
42
+ self.subnet = SubnetRpcClient(substrate)
43
+ self.wallet = WalletRpcClient(substrate)
44
+ self.node = SubnetNodeRpcClient(substrate)
45
+ self.staking = StakingRpcClient(substrate)
46
+ self.overwatch = OverwatchRpcClient(substrate)
47
+ self.identity = IdentityExtrinsics(substrate)
48
+
49
+
50
+ class ExtrinsicsLayer:
51
+ """Container for Extrinsics (state-changing) operations."""
52
+
53
+ def __init__(self, substrate):
54
+ self.subnet = SubnetExtrinsics(substrate)
55
+ self.wallet = WalletExtrinsics(substrate)
56
+ self.node = NodeExtrinsics(substrate)
57
+ self.governance = GovernanceExtrinsics(substrate)
58
+ self.identity = IdentityExtrinsics(substrate)
59
+ self.staking = StakingExtrinsics(substrate)
60
+ self.overwatch = OverwatchExtrinsics(substrate)
61
+ self.consensus = ConsensusExtrinsics(substrate)
62
+ self.validator = ValidatorExtrinsics(substrate)
63
+
64
+
65
+ class OffchainLayer:
66
+ """Container for Off-chain (local) operations."""
67
+
68
+ def __init__(self, config=None):
69
+ # Get wallet path from config if available
70
+ wallet_dir = None
71
+ if config and config.wallet and config.wallet.path:
72
+ wallet_dir = str(Path(config.wallet.path).expanduser())
73
+ self.wallet = WalletManager(wallet_dir=wallet_dir)
74
+ self.config = ConfigManager()
75
+ self.backup = BackupManager()
76
+
77
+
78
+ class HypertensorClient:
79
+ """
80
+ Minimal client that manages blockchain connection and exposes 3-layer architecture.
81
+
82
+ Usage:
83
+ client.rpc.chain.get_network_stats()
84
+ client.extrinsics.wallet.add_to_stake(request, keypair)
85
+ client.offchain.keystore.get_or_create_wallet_session(wallet_name)
86
+ """
87
+
88
+ def __init__(self, config):
89
+ self.config = config
90
+ self.substrate = None
91
+ self.ws_connection = None
92
+
93
+ # The 3 layers - initialized only after successful connection
94
+ # Initialize offchain immediately (doesn't need blockchain connection)
95
+ self.offchain = OffchainLayer(config=config)
96
+ self.rpc = None # Will be initialized in connect()
97
+ self.extrinsics = None # Will be initialized in connect()
98
+
99
+ def connect(
100
+ self, rpc_url: Optional[str] = None, force_reconnect: bool = False
101
+ ) -> bool:
102
+ """Connect to the Hypertensor blockchain and initialize the 3 layers."""
103
+ try:
104
+ # Close existing connection if force reconnecting
105
+ if force_reconnect and self.substrate:
106
+ try:
107
+ self.substrate.close()
108
+ except Exception:
109
+ pass # Ignore errors during close
110
+ self.substrate = None
111
+ self.rpc = None
112
+ self.extrinsics = None
113
+
114
+ url = rpc_url or self.config.network.endpoint
115
+ logger.info(f"Attempting to connect to blockchain at {url}")
116
+
117
+ # Create direct blockchain connection
118
+ logger.info("Creating direct blockchain connection")
119
+ # SubstrateInterface handles WebSocket connections internally
120
+ # We'll create it with the URL and let it manage the connection
121
+ try:
122
+ # Load custom type registry for encoding/decoding
123
+ from scalecodec.type_registry import load_type_registry_preset
124
+
125
+ from ..utils.blockchain.type_registry import CUSTOM_RPC_TYPE_REGISTRY
126
+
127
+ # Merge custom types with legacy preset
128
+ type_registry = load_type_registry_preset("legacy")
129
+ type_registry.update(CUSTOM_RPC_TYPE_REGISTRY)
130
+
131
+ # Try with use_remote_preset for faster metadata loading
132
+ self.substrate = SubstrateInterface(
133
+ url=url,
134
+ use_remote_preset=True, # Use remote metadata preset for faster init
135
+ type_registry=type_registry, # Add custom type registry
136
+ )
137
+ except Exception as init_error:
138
+ logger.warning(
139
+ f"Connection with remote preset failed: {init_error}, trying without preset..."
140
+ )
141
+ # Fallback to basic connection without remote preset
142
+ try:
143
+ from scalecodec.type_registry import load_type_registry_preset
144
+
145
+ from ..utils.blockchain.type_registry import (
146
+ CUSTOM_RPC_TYPE_REGISTRY,
147
+ )
148
+
149
+ type_registry = load_type_registry_preset("legacy")
150
+ type_registry.update(CUSTOM_RPC_TYPE_REGISTRY)
151
+
152
+ self.substrate = SubstrateInterface(
153
+ url=url,
154
+ type_registry=type_registry, # Add custom type registry
155
+ )
156
+ except Exception as fallback_error:
157
+ logger.error(
158
+ f"Connection failed even without preset: {fallback_error}"
159
+ )
160
+ raise
161
+
162
+ # Initialize runtime metadata (required for queries to work)
163
+ # This must be done before any queries to ensure metadata is loaded
164
+ # Add timeout to prevent hanging indefinitely
165
+ try:
166
+ import signal
167
+
168
+ timeout_seconds = self.config.network.timeout or 30
169
+
170
+ def timeout_handler(signum, frame):
171
+ raise TimeoutError(
172
+ f"Runtime initialization timed out after {timeout_seconds}s"
173
+ )
174
+
175
+ # Set timeout (Unix only - Windows will skip this)
176
+ try:
177
+ old_handler = signal.signal(signal.SIGALRM, timeout_handler)
178
+ signal.alarm(timeout_seconds)
179
+ except (AttributeError, ValueError):
180
+ # Windows doesn't support SIGALRM, skip timeout
181
+ old_handler = None
182
+
183
+ try:
184
+ self.substrate.init_runtime()
185
+ logger.debug("Runtime metadata initialized successfully")
186
+ finally:
187
+ # Cancel timeout
188
+ if old_handler is not None:
189
+ try:
190
+ signal.alarm(0)
191
+ signal.signal(signal.SIGALRM, old_handler)
192
+ except (AttributeError, ValueError):
193
+ pass
194
+ except TimeoutError as timeout_error:
195
+ logger.error(f"Runtime initialization timed out: {timeout_error}")
196
+ raise Exception(
197
+ f"Cannot connect: metadata initialization timed out after {timeout_seconds}s. "
198
+ f"This usually means the connection is very slow or the endpoint is unreachable."
199
+ ) from timeout_error
200
+ except Exception as meta_error:
201
+ logger.error(f"Failed to initialize runtime metadata: {meta_error}")
202
+ # Don't continue if metadata init fails - queries won't work
203
+ raise Exception(
204
+ f"Cannot connect: metadata initialization failed: {meta_error}"
205
+ ) from meta_error
206
+
207
+ # Test the connection with a simple query to verify metadata is loaded
208
+ try:
209
+ # Try a simple query to ensure metadata is working
210
+ _ = self.substrate.query(
211
+ "System", "Account", ["0x0000000000000000000000000000000000000000"]
212
+ )
213
+ chain_properties = self.substrate.properties
214
+ logger.info(f"Connected to blockchain at {url}")
215
+ logger.info(f"Chain: {chain_properties.get('tokenSymbol', 'Unknown')}")
216
+ logger.debug("Connection test query successful - metadata is loaded")
217
+ except Exception as test_error:
218
+ logger.error(f"Connection test failed: {test_error}")
219
+ # If test query fails, connection is not usable
220
+ raise Exception(
221
+ f"Cannot connect: test query failed: {test_error}"
222
+ ) from test_error
223
+
224
+ # Initialize the 3 layers only if connection successful
225
+ if self.substrate:
226
+ self.rpc = RPCLayer(self.substrate)
227
+ self.extrinsics = ExtrinsicsLayer(self.substrate)
228
+ self.offchain = OffchainLayer(config=self.config)
229
+ return True
230
+ else:
231
+ logger.error("Substrate connection not established")
232
+ return False
233
+
234
+ except Exception as e:
235
+ logger.error(f"Failed to connect to blockchain: {e}")
236
+ logger.error("Connection details:")
237
+ logger.error(f" URL: {url}")
238
+ logger.error(f" Timeout: {self.config.network.timeout}s")
239
+ logger.error(f" Network type: {self.config.network.network_type}")
240
+
241
+ # Provide helpful error message for network issues
242
+ if "Temporary failure in name resolution" in str(e):
243
+ logger.error("\n💡 Network connection issue detected:")
244
+ logger.error(" The blockchain endpoint is unreachable.")
245
+ logger.error(" This could be due to:")
246
+ logger.error(" • Network connectivity issues")
247
+ logger.error(" • DNS resolution problems")
248
+ logger.error(" • The endpoint being temporarily down")
249
+ logger.error("\n Try:")
250
+ logger.error(" • Check your internet connection")
251
+ logger.error(" • Try a different network")
252
+ logger.error(" • Contact the network administrator")
253
+
254
+ # Don't initialize layers with None - they should be None until connected
255
+ # This prevents trying to use RPC clients without a valid connection
256
+ self.rpc = None
257
+ self.extrinsics = None
258
+ self.offchain = OffchainLayer(
259
+ config=self.config
260
+ ) # Offchain doesn't need blockchain
261
+ return False
262
+
263
+ def is_connected(self) -> bool:
264
+ """
265
+ Lightweight check if client appears connected to blockchain.
266
+
267
+ This does NOT perform expensive queries - it only checks connection state.
268
+ For actual verification, use verify_connection() which does a query.
269
+ """
270
+ if not self.substrate or not self.rpc or not self.extrinsics:
271
+ return False
272
+
273
+ try:
274
+ # Check WebSocket connection status if available
275
+ # SubstrateInterface uses websockets which can timeout
276
+ if hasattr(self.substrate, "websocket") and self.substrate.websocket:
277
+ if hasattr(self.substrate.websocket, "connected"):
278
+ if not self.substrate.websocket.connected:
279
+ logger.debug(
280
+ "WebSocket connection check failed - not connected"
281
+ )
282
+ return False
283
+ elif hasattr(self.substrate.websocket, "sock"):
284
+ # Alternative check: see if socket exists
285
+ if self.substrate.websocket.sock is None:
286
+ logger.debug("WebSocket socket check failed - no socket")
287
+ return False
288
+
289
+ # Lightweight check: just verify properties exist (no network call)
290
+ # This is much faster than a query
291
+ try:
292
+ _ = self.substrate.properties
293
+ return True
294
+ except Exception:
295
+ return False
296
+ except (AttributeError, Exception) as e:
297
+ # Connection appears dead
298
+ logger.debug(f"Lightweight connection check failed: {e}")
299
+ return False
300
+
301
+ def verify_connection(self) -> bool:
302
+ """
303
+ Perform an actual query to verify the connection is working.
304
+ This is more expensive but more reliable than is_connected().
305
+ Use this when you need to be certain the connection works.
306
+ """
307
+ if not self.is_connected():
308
+ return False
309
+
310
+ try:
311
+ # Do a lightweight query to verify connection actually works
312
+ _ = self.substrate.query(
313
+ "System", "Account", ["0x0000000000000000000000000000000000000000"]
314
+ )
315
+ return True
316
+ except Exception as query_error:
317
+ logger.debug(f"Connection verification query failed: {query_error}")
318
+ return False
319
+
320
+ def disconnect(self):
321
+ """Disconnect from blockchain."""
322
+ if self.substrate:
323
+ try:
324
+ self.substrate.close()
325
+ except Exception:
326
+ pass # Ignore errors during disconnect
327
+ if self.ws_connection:
328
+ try:
329
+ self.ws_connection.close()
330
+ except Exception:
331
+ pass # Ignore errors during disconnect
332
+ # Reset layers
333
+ self.rpc = None
334
+ self.extrinsics = None
335
+ self.substrate = None
336
+
337
+
338
+ __all__ = ["HypertensorClient"]
@@ -0,0 +1,26 @@
1
+ """
2
+ Extrinsics module for blockchain state-changing operations.
3
+ Contains clients for submitting transactions that modify blockchain state.
4
+ """
5
+
6
+ from .consensus import ConsensusExtrinsics
7
+ from .governance import GovernanceExtrinsics
8
+ from .identity import IdentityExtrinsics
9
+ from .node import NodeExtrinsics
10
+ from .overwatch import OverwatchExtrinsics
11
+ from .staking import StakingExtrinsics
12
+ from .subnet import SubnetExtrinsics
13
+ from .validator import ValidatorExtrinsics
14
+ from .wallet import WalletExtrinsics
15
+
16
+ __all__ = [
17
+ "SubnetExtrinsics",
18
+ "WalletExtrinsics",
19
+ "NodeExtrinsics",
20
+ "StakingExtrinsics",
21
+ "IdentityExtrinsics",
22
+ "GovernanceExtrinsics",
23
+ "OverwatchExtrinsics",
24
+ "ConsensusExtrinsics",
25
+ "ValidatorExtrinsics",
26
+ ]