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,179 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Blockchain validation utilities with EVM/ECDSA focus.
|
|
3
|
+
Validates addresses, amounts, and other blockchain-related inputs.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import re
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Optional, Union
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def validate_address(address: str) -> bool:
|
|
12
|
+
"""Validate an EVM/Ethereum address format."""
|
|
13
|
+
if not address:
|
|
14
|
+
return False
|
|
15
|
+
if address.startswith("0x") and len(address) == 42:
|
|
16
|
+
try:
|
|
17
|
+
int(address[2:], 16)
|
|
18
|
+
return True
|
|
19
|
+
except ValueError:
|
|
20
|
+
return False
|
|
21
|
+
return False
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def validate_ethereum_address(address: str) -> bool:
|
|
25
|
+
"""Validate Ethereum address format (alias for validate_address)."""
|
|
26
|
+
return validate_address(address)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def validate_hotkey_address(address: str) -> bool:
|
|
30
|
+
"""Validate hotkey address - should be Bytes20 format for EVM compatibility."""
|
|
31
|
+
return validate_ethereum_address(address)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def validate_amount(amount: Union[str, float, int]) -> bool:
|
|
35
|
+
"""Validate a token amount."""
|
|
36
|
+
try:
|
|
37
|
+
if isinstance(amount, str):
|
|
38
|
+
amount = float(amount)
|
|
39
|
+
return isinstance(amount, (int, float)) and amount > 0
|
|
40
|
+
except (ValueError, TypeError):
|
|
41
|
+
return False
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def validate_subnet_id(subnet_id: Union[str, int]) -> bool:
|
|
45
|
+
"""Validate a subnet ID."""
|
|
46
|
+
try:
|
|
47
|
+
if isinstance(subnet_id, str):
|
|
48
|
+
subnet_id = int(subnet_id)
|
|
49
|
+
return isinstance(subnet_id, int) and subnet_id >= 0
|
|
50
|
+
except (ValueError, TypeError):
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def validate_node_id(node_id: Union[str, int]) -> bool:
|
|
55
|
+
"""Validate a node ID."""
|
|
56
|
+
try:
|
|
57
|
+
if isinstance(node_id, str):
|
|
58
|
+
node_id = int(node_id)
|
|
59
|
+
return isinstance(node_id, int) and node_id >= 0
|
|
60
|
+
except (ValueError, TypeError):
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def validate_peer_id(peer_id: str) -> bool:
|
|
65
|
+
"""Validate a peer ID format."""
|
|
66
|
+
if not peer_id or not isinstance(peer_id, str):
|
|
67
|
+
return False
|
|
68
|
+
valid_prefixes = ["12D3KooW", "Qm", "1"]
|
|
69
|
+
return any(peer_id.startswith(prefix) for prefix in valid_prefixes)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def validate_key_type(key_type: str) -> bool:
|
|
73
|
+
"""Validate cryptographic key type."""
|
|
74
|
+
valid_types = ["ecdsa", "secp256k1", "sr25519", "ed25519"]
|
|
75
|
+
return key_type.lower() in valid_types
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def validate_password(password: Optional[str]) -> bool:
|
|
79
|
+
"""Validate password strength."""
|
|
80
|
+
if password is None:
|
|
81
|
+
return True
|
|
82
|
+
if not isinstance(password, str) or len(password) < 8:
|
|
83
|
+
return False
|
|
84
|
+
return True
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def validate_file_path(file_path: str) -> bool:
|
|
88
|
+
"""Validate file path format and accessibility."""
|
|
89
|
+
try:
|
|
90
|
+
path = Path(file_path)
|
|
91
|
+
if not str(path):
|
|
92
|
+
return False
|
|
93
|
+
parent = path.parent
|
|
94
|
+
return parent.exists() or parent == Path(".")
|
|
95
|
+
except Exception:
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def validate_wallet_name(wallet_name: str) -> bool:
|
|
100
|
+
"""Validate wallet name format."""
|
|
101
|
+
if not wallet_name or not isinstance(wallet_name, str):
|
|
102
|
+
return False
|
|
103
|
+
pattern = r"^[a-zA-Z0-9_-]+$"
|
|
104
|
+
if not re.match(pattern, wallet_name):
|
|
105
|
+
return False
|
|
106
|
+
if len(wallet_name) < 1 or len(wallet_name) > 64:
|
|
107
|
+
return False
|
|
108
|
+
if wallet_name.startswith(("-", "_")) or wallet_name.endswith(("-", "_")):
|
|
109
|
+
return False
|
|
110
|
+
return True
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def validate_network_url(url: str) -> bool:
|
|
114
|
+
"""Validate network URL format."""
|
|
115
|
+
if not url or not isinstance(url, str):
|
|
116
|
+
return False
|
|
117
|
+
valid_schemes = ["ws://", "wss://", "http://", "https://"]
|
|
118
|
+
return any(url.startswith(scheme) for scheme in valid_schemes)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def validate_hex_string(hex_str: str, expected_length: Optional[int] = None) -> bool:
|
|
122
|
+
"""Validate hexadecimal string format."""
|
|
123
|
+
if not hex_str or not isinstance(hex_str, str):
|
|
124
|
+
return False
|
|
125
|
+
if hex_str.startswith("0x"):
|
|
126
|
+
hex_str = hex_str[2:]
|
|
127
|
+
try:
|
|
128
|
+
int(hex_str, 16)
|
|
129
|
+
except ValueError:
|
|
130
|
+
return False
|
|
131
|
+
if expected_length is not None:
|
|
132
|
+
return len(hex_str) == expected_length
|
|
133
|
+
return True
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def validate_mnemonic(mnemonic: str) -> bool:
|
|
137
|
+
"""Validate mnemonic phrase format."""
|
|
138
|
+
if not mnemonic or not isinstance(mnemonic, str):
|
|
139
|
+
return False
|
|
140
|
+
words = mnemonic.strip().split()
|
|
141
|
+
valid_lengths = [12, 15, 18, 21, 24]
|
|
142
|
+
if len(words) not in valid_lengths:
|
|
143
|
+
return False
|
|
144
|
+
for word in words:
|
|
145
|
+
if not re.match(r"^[a-z]+$", word):
|
|
146
|
+
return False
|
|
147
|
+
return True
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def validate_tensor_amount(amount: Union[str, float, int]) -> bool:
|
|
151
|
+
"""Validate TENSOR token amount."""
|
|
152
|
+
try:
|
|
153
|
+
from .formatting import TensorAmount
|
|
154
|
+
|
|
155
|
+
tensor_amount = TensorAmount(amount)
|
|
156
|
+
return tensor_amount.is_valid()
|
|
157
|
+
except (ValueError, TypeError):
|
|
158
|
+
return False
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def detect_address_type(address: str) -> tuple[str, str, bool]:
|
|
162
|
+
"""Detect the type and format of an address."""
|
|
163
|
+
if not address:
|
|
164
|
+
return "unknown", "Invalid", False
|
|
165
|
+
if address.startswith("0x"):
|
|
166
|
+
if len(address) == 42:
|
|
167
|
+
return "ecdsa", "EVM/ECDSA (20-byte)", True
|
|
168
|
+
else:
|
|
169
|
+
return (
|
|
170
|
+
"invalid_evm",
|
|
171
|
+
f"Invalid EVM (expected 42 chars, got {len(address)})",
|
|
172
|
+
False,
|
|
173
|
+
)
|
|
174
|
+
elif len(address) > 40 and address[0].isdigit():
|
|
175
|
+
return "ss58", "SS58/Substrate (32-byte)", False
|
|
176
|
+
elif len(address) < 20:
|
|
177
|
+
return "unknown", "Too Short", False
|
|
178
|
+
else:
|
|
179
|
+
return "unknown", "Unknown Format", False
|