hippius 0.2.10__tar.gz → 0.2.12__tar.gz
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.
- {hippius-0.2.10 → hippius-0.2.12}/PKG-INFO +1 -1
- {hippius-0.2.10 → hippius-0.2.12}/hippius_sdk/__init__.py +1 -1
- {hippius-0.2.10 → hippius-0.2.12}/hippius_sdk/cli_handlers.py +76 -49
- {hippius-0.2.10 → hippius-0.2.12}/hippius_sdk/client.py +63 -15
- {hippius-0.2.10 → hippius-0.2.12}/hippius_sdk/config.py +14 -2
- {hippius-0.2.10 → hippius-0.2.12}/hippius_sdk/ipfs.py +162 -36
- {hippius-0.2.10 → hippius-0.2.12}/hippius_sdk/substrate.py +123 -79
- {hippius-0.2.10 → hippius-0.2.12}/hippius_sdk/utils.py +14 -8
- {hippius-0.2.10 → hippius-0.2.12}/pyproject.toml +1 -1
- {hippius-0.2.10 → hippius-0.2.12}/README.md +0 -0
- {hippius-0.2.10 → hippius-0.2.12}/hippius_sdk/cli.py +0 -0
- {hippius-0.2.10 → hippius-0.2.12}/hippius_sdk/cli_assets.py +0 -0
- {hippius-0.2.10 → hippius-0.2.12}/hippius_sdk/cli_parser.py +0 -0
- {hippius-0.2.10 → hippius-0.2.12}/hippius_sdk/cli_rich.py +0 -0
- {hippius-0.2.10 → hippius-0.2.12}/hippius_sdk/errors.py +0 -0
- {hippius-0.2.10 → hippius-0.2.12}/hippius_sdk/ipfs_core.py +0 -0
@@ -102,20 +102,38 @@ def create_client(args: Any) -> HippiusClient:
|
|
102
102
|
# Get substrate URL
|
103
103
|
substrate_url = args.substrate_url if hasattr(args, "substrate_url") else None
|
104
104
|
|
105
|
-
#
|
106
|
-
#
|
105
|
+
# Determine if we need to use password based on the command
|
106
|
+
# Only use password for: store, download, delete, erasure-code (unless --no-publish), reconstruct
|
107
107
|
password = None
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
):
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
#
|
118
|
-
|
108
|
+
|
109
|
+
# First check if password is provided as an argument
|
110
|
+
if hasattr(args, "password") and args.password:
|
111
|
+
password = args.password
|
112
|
+
# Otherwise, decide based on the command
|
113
|
+
elif hasattr(args, "command"):
|
114
|
+
command = args.command
|
115
|
+
needs_password = False
|
116
|
+
|
117
|
+
# Check if this is one of the commands that needs a password
|
118
|
+
if command in [
|
119
|
+
"store",
|
120
|
+
"store-dir",
|
121
|
+
"download",
|
122
|
+
"delete",
|
123
|
+
"delete-dir",
|
124
|
+
"reconstruct",
|
125
|
+
]:
|
126
|
+
needs_password = True
|
127
|
+
# Special case for erasure-code - only needs password if we're publishing
|
128
|
+
elif command == "erasure-code" and not (
|
129
|
+
hasattr(args, "no_publish") and args.no_publish
|
130
|
+
):
|
131
|
+
needs_password = True
|
132
|
+
|
133
|
+
# If this command doesn't need password access, set to empty string to skip prompting
|
134
|
+
if not needs_password:
|
135
|
+
# Use empty string to indicate "skip password prompt" to the config system
|
136
|
+
password = ""
|
119
137
|
|
120
138
|
# Initialize client with provided parameters
|
121
139
|
client = HippiusClient(
|
@@ -692,27 +710,32 @@ async def handle_credits(
|
|
692
710
|
):
|
693
711
|
account_address = client.substrate_client._keypair.ss58_address
|
694
712
|
else:
|
695
|
-
#
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
if has_default:
|
705
|
-
warning(
|
706
|
-
"Please provide an account address with '--account_address' or the default address may be invalid."
|
707
|
-
)
|
713
|
+
# Get the active account name and its address
|
714
|
+
from hippius_sdk.config import get_account_address, get_active_account
|
715
|
+
|
716
|
+
active_account = get_active_account()
|
717
|
+
if active_account:
|
718
|
+
active_address = get_account_address(active_account)
|
719
|
+
if active_address:
|
720
|
+
account_address = active_address
|
708
721
|
else:
|
709
|
-
|
710
|
-
"
|
722
|
+
error(
|
723
|
+
f"Active account '{active_account}' does not have a valid address."
|
711
724
|
)
|
712
|
-
|
713
|
-
"
|
725
|
+
warning(
|
726
|
+
"Please provide an account address with '--account_address'"
|
714
727
|
)
|
715
|
-
|
728
|
+
return 1
|
729
|
+
else:
|
730
|
+
error(
|
731
|
+
"No account address provided, no active account set, and client has no keypair."
|
732
|
+
)
|
733
|
+
warning(
|
734
|
+
"Please provide an account address with '--account_address' or set an active account with:"
|
735
|
+
)
|
736
|
+
log(
|
737
|
+
" [bold green underline]hippius account switch <account_name>[/bold green underline]"
|
738
|
+
)
|
716
739
|
return 1
|
717
740
|
|
718
741
|
credits = await client.substrate_client.get_free_credits(account_address)
|
@@ -3160,28 +3183,32 @@ async def handle_account_balance(
|
|
3160
3183
|
):
|
3161
3184
|
account_address = client.substrate_client._keypair.ss58_address
|
3162
3185
|
else:
|
3163
|
-
#
|
3164
|
-
|
3165
|
-
if default_address:
|
3166
|
-
account_address = default_address
|
3167
|
-
else:
|
3168
|
-
has_default = get_default_address() is not None
|
3186
|
+
# Get the active account name and its address
|
3187
|
+
from hippius_sdk.config import get_account_address, get_active_account
|
3169
3188
|
|
3170
|
-
|
3171
|
-
|
3172
|
-
|
3173
|
-
|
3174
|
-
|
3175
|
-
)
|
3189
|
+
active_account = get_active_account()
|
3190
|
+
if active_account:
|
3191
|
+
active_address = get_account_address(active_account)
|
3192
|
+
if active_address:
|
3193
|
+
account_address = active_address
|
3176
3194
|
else:
|
3177
|
-
|
3178
|
-
"
|
3195
|
+
error(
|
3196
|
+
f"Active account '{active_account}' does not have a valid address."
|
3179
3197
|
)
|
3180
|
-
|
3181
|
-
"
|
3182
|
-
style="bold blue",
|
3198
|
+
warning(
|
3199
|
+
"Please provide an account address with '--account_address'"
|
3183
3200
|
)
|
3184
|
-
|
3201
|
+
return 1
|
3202
|
+
else:
|
3203
|
+
error(
|
3204
|
+
"No account address provided, no active account set, and client has no keypair."
|
3205
|
+
)
|
3206
|
+
warning(
|
3207
|
+
"Please provide an account address with '--account_address' or set an active account with:"
|
3208
|
+
)
|
3209
|
+
log(
|
3210
|
+
" [bold green underline]hippius account switch <account_name>[/bold green underline]"
|
3211
|
+
)
|
3185
3212
|
return 1
|
3186
3213
|
|
3187
3214
|
# Get the account balance
|
@@ -85,13 +85,15 @@ class HippiusClient:
|
|
85
85
|
# Initialize Substrate client
|
86
86
|
self.substrate_client = SubstrateClient(
|
87
87
|
url=substrate_url,
|
88
|
-
seed_phrase=substrate_seed_phrase,
|
89
88
|
password=seed_phrase_password,
|
90
89
|
account_name=account_name,
|
91
90
|
)
|
92
91
|
|
93
92
|
async def upload_file(
|
94
|
-
self,
|
93
|
+
self,
|
94
|
+
file_path: str,
|
95
|
+
encrypt: Optional[bool] = None,
|
96
|
+
seed_phrase: Optional[str] = None,
|
95
97
|
) -> Dict[str, Any]:
|
96
98
|
"""
|
97
99
|
Upload a file to IPFS with optional encryption.
|
@@ -99,6 +101,7 @@ class HippiusClient:
|
|
99
101
|
Args:
|
100
102
|
file_path: Path to the file to upload
|
101
103
|
encrypt: Whether to encrypt the file (overrides default)
|
104
|
+
seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
|
102
105
|
|
103
106
|
Returns:
|
104
107
|
Dict[str, Any]: Dictionary containing file details including:
|
@@ -114,10 +117,15 @@ class HippiusClient:
|
|
114
117
|
ValueError: If encryption is requested but not available
|
115
118
|
"""
|
116
119
|
# Use the enhanced IPFSClient method directly with encryption parameter
|
117
|
-
return await self.ipfs_client.upload_file(
|
120
|
+
return await self.ipfs_client.upload_file(
|
121
|
+
file_path, encrypt=encrypt, seed_phrase=seed_phrase
|
122
|
+
)
|
118
123
|
|
119
124
|
async def upload_directory(
|
120
|
-
self,
|
125
|
+
self,
|
126
|
+
dir_path: str,
|
127
|
+
encrypt: Optional[bool] = None,
|
128
|
+
seed_phrase: Optional[str] = None,
|
121
129
|
) -> Dict[str, Any]:
|
122
130
|
"""
|
123
131
|
Upload a directory to IPFS with optional encryption.
|
@@ -125,6 +133,7 @@ class HippiusClient:
|
|
125
133
|
Args:
|
126
134
|
dir_path: Path to the directory to upload
|
127
135
|
encrypt: Whether to encrypt files (overrides default)
|
136
|
+
seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
|
128
137
|
|
129
138
|
Returns:
|
130
139
|
Dict[str, Any]: Dictionary containing directory details including:
|
@@ -141,10 +150,16 @@ class HippiusClient:
|
|
141
150
|
ValueError: If encryption is requested but not available
|
142
151
|
"""
|
143
152
|
# Use the enhanced IPFSClient method directly with encryption parameter
|
144
|
-
return await self.ipfs_client.upload_directory(
|
153
|
+
return await self.ipfs_client.upload_directory(
|
154
|
+
dir_path, encrypt=encrypt, seed_phrase=seed_phrase
|
155
|
+
)
|
145
156
|
|
146
157
|
async def download_file(
|
147
|
-
self,
|
158
|
+
self,
|
159
|
+
cid: str,
|
160
|
+
output_path: str,
|
161
|
+
decrypt: Optional[bool] = None,
|
162
|
+
seed_phrase: Optional[str] = None,
|
148
163
|
) -> Dict[str, Any]:
|
149
164
|
"""
|
150
165
|
Download a file from IPFS with optional decryption.
|
@@ -154,6 +169,7 @@ class HippiusClient:
|
|
154
169
|
cid: Content Identifier (CID) of the file to download
|
155
170
|
output_path: Path where the downloaded file/directory will be saved
|
156
171
|
decrypt: Whether to decrypt the file (overrides default)
|
172
|
+
seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
|
157
173
|
|
158
174
|
Returns:
|
159
175
|
Dict[str, Any]: Dictionary containing download details including:
|
@@ -169,7 +185,9 @@ class HippiusClient:
|
|
169
185
|
requests.RequestException: If the download fails
|
170
186
|
ValueError: If decryption is requested but fails
|
171
187
|
"""
|
172
|
-
return await self.ipfs_client.download_file(
|
188
|
+
return await self.ipfs_client.download_file(
|
189
|
+
cid, output_path, _=decrypt, seed_phrase=seed_phrase
|
190
|
+
)
|
173
191
|
|
174
192
|
async def cat(
|
175
193
|
self,
|
@@ -177,6 +195,7 @@ class HippiusClient:
|
|
177
195
|
max_display_bytes: int = 1024,
|
178
196
|
format_output: bool = True,
|
179
197
|
decrypt: Optional[bool] = None,
|
198
|
+
seed_phrase: Optional[str] = None,
|
180
199
|
) -> Dict[str, Any]:
|
181
200
|
"""
|
182
201
|
Get the content of a file from IPFS with optional decryption.
|
@@ -186,6 +205,7 @@ class HippiusClient:
|
|
186
205
|
max_display_bytes: Maximum number of bytes to include in the preview
|
187
206
|
format_output: Whether to attempt to decode the content as text
|
188
207
|
decrypt: Whether to decrypt the file (overrides default)
|
208
|
+
seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
|
189
209
|
|
190
210
|
Returns:
|
191
211
|
Dict[str, Any]: Dictionary containing content details including:
|
@@ -197,15 +217,22 @@ class HippiusClient:
|
|
197
217
|
- decrypted: Whether the file was decrypted
|
198
218
|
"""
|
199
219
|
return await self.ipfs_client.cat(
|
200
|
-
cid,
|
220
|
+
cid,
|
221
|
+
max_display_bytes,
|
222
|
+
format_output,
|
223
|
+
decrypt=decrypt,
|
224
|
+
seed_phrase=seed_phrase,
|
201
225
|
)
|
202
226
|
|
203
|
-
async def exists(
|
227
|
+
async def exists(
|
228
|
+
self, cid: str, seed_phrase: Optional[str] = None
|
229
|
+
) -> Dict[str, Any]:
|
204
230
|
"""
|
205
231
|
Check if a CID exists on IPFS.
|
206
232
|
|
207
233
|
Args:
|
208
234
|
cid: Content Identifier (CID) to check
|
235
|
+
seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
|
209
236
|
|
210
237
|
Returns:
|
211
238
|
Dict[str, Any]: Dictionary containing:
|
@@ -214,14 +241,15 @@ class HippiusClient:
|
|
214
241
|
- formatted_cid: Formatted version of the CID
|
215
242
|
- gateway_url: URL to access the content if it exists
|
216
243
|
"""
|
217
|
-
return await self.ipfs_client.exists(cid)
|
244
|
+
return await self.ipfs_client.exists(cid, seed_phrase=seed_phrase)
|
218
245
|
|
219
|
-
async def pin(self, cid: str) -> Dict[str, Any]:
|
246
|
+
async def pin(self, cid: str, seed_phrase: Optional[str] = None) -> Dict[str, Any]:
|
220
247
|
"""
|
221
248
|
Pin a CID to IPFS to keep it available.
|
222
249
|
|
223
250
|
Args:
|
224
251
|
cid: Content Identifier (CID) to pin
|
252
|
+
seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
|
225
253
|
|
226
254
|
Returns:
|
227
255
|
Dict[str, Any]: Dictionary containing:
|
@@ -230,7 +258,7 @@ class HippiusClient:
|
|
230
258
|
- formatted_cid: Formatted version of the CID
|
231
259
|
- message: Status message
|
232
260
|
"""
|
233
|
-
return await self.ipfs_client.pin(cid)
|
261
|
+
return await self.ipfs_client.pin(cid, seed_phrase=seed_phrase)
|
234
262
|
|
235
263
|
def format_cid(self, cid: str) -> str:
|
236
264
|
"""
|
@@ -293,6 +321,7 @@ class HippiusClient:
|
|
293
321
|
encrypt: Optional[bool] = None,
|
294
322
|
max_retries: int = 3,
|
295
323
|
verbose: bool = True,
|
324
|
+
seed_phrase: Optional[str] = None,
|
296
325
|
) -> Dict[str, Any]:
|
297
326
|
"""
|
298
327
|
Split a file using erasure coding, then upload the chunks to IPFS.
|
@@ -310,6 +339,7 @@ class HippiusClient:
|
|
310
339
|
encrypt: Whether to encrypt the file before encoding (defaults to self.encrypt_by_default)
|
311
340
|
max_retries: Maximum number of retry attempts for IPFS uploads
|
312
341
|
verbose: Whether to print progress information
|
342
|
+
seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
|
313
343
|
|
314
344
|
Returns:
|
315
345
|
dict: Metadata including the original file info and chunk information
|
@@ -326,6 +356,7 @@ class HippiusClient:
|
|
326
356
|
encrypt=encrypt,
|
327
357
|
max_retries=max_retries,
|
328
358
|
verbose=verbose,
|
359
|
+
seed_phrase=seed_phrase,
|
329
360
|
)
|
330
361
|
|
331
362
|
async def reconstruct_from_erasure_code(
|
@@ -335,6 +366,7 @@ class HippiusClient:
|
|
335
366
|
temp_dir: str = None,
|
336
367
|
max_retries: int = 3,
|
337
368
|
verbose: bool = True,
|
369
|
+
seed_phrase: Optional[str] = None,
|
338
370
|
) -> Dict:
|
339
371
|
"""
|
340
372
|
Reconstruct a file from erasure-coded chunks using its metadata.
|
@@ -345,6 +377,7 @@ class HippiusClient:
|
|
345
377
|
temp_dir: Directory to use for temporary files (default: system temp)
|
346
378
|
max_retries: Maximum number of retry attempts for IPFS downloads
|
347
379
|
verbose: Whether to print progress information
|
380
|
+
seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
|
348
381
|
|
349
382
|
Returns:
|
350
383
|
Dict: containing file reconstruction info.
|
@@ -359,6 +392,7 @@ class HippiusClient:
|
|
359
392
|
temp_dir=temp_dir,
|
360
393
|
max_retries=max_retries,
|
361
394
|
verbose=verbose,
|
395
|
+
seed_phrase=seed_phrase,
|
362
396
|
)
|
363
397
|
|
364
398
|
async def store_erasure_coded_file(
|
@@ -373,6 +407,7 @@ class HippiusClient:
|
|
373
407
|
verbose: bool = True,
|
374
408
|
progress_callback: Optional[Callable[[str, int, int], None]] = None,
|
375
409
|
publish: bool = True,
|
410
|
+
seed_phrase: Optional[str] = None,
|
376
411
|
) -> Dict[str, Any]:
|
377
412
|
"""
|
378
413
|
Erasure code a file, upload the chunks to IPFS, and store in the Hippius marketplace.
|
@@ -393,6 +428,7 @@ class HippiusClient:
|
|
393
428
|
publish: Whether to publish to the blockchain (True) or just perform local
|
394
429
|
erasure coding without publishing (False). When False, no password
|
395
430
|
is needed for seed phrase access.
|
431
|
+
seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
|
396
432
|
|
397
433
|
Returns:
|
398
434
|
dict: Result including metadata CID and transaction hash (if published)
|
@@ -413,10 +449,14 @@ class HippiusClient:
|
|
413
449
|
verbose=verbose,
|
414
450
|
progress_callback=progress_callback,
|
415
451
|
publish=publish,
|
452
|
+
seed_phrase=seed_phrase,
|
416
453
|
)
|
417
454
|
|
418
455
|
async def delete_file(
|
419
|
-
self,
|
456
|
+
self,
|
457
|
+
cid: str,
|
458
|
+
cancel_from_blockchain: bool = True,
|
459
|
+
seed_phrase: Optional[str] = None,
|
420
460
|
) -> Dict[str, Any]:
|
421
461
|
"""
|
422
462
|
Delete a file from IPFS and optionally cancel its storage on the blockchain.
|
@@ -424,6 +464,7 @@ class HippiusClient:
|
|
424
464
|
Args:
|
425
465
|
cid: Content Identifier (CID) of the file to delete
|
426
466
|
cancel_from_blockchain: Whether to also cancel the storage request from the blockchain
|
467
|
+
seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
|
427
468
|
|
428
469
|
Returns:
|
429
470
|
Dict containing the result of the operation
|
@@ -431,13 +472,16 @@ class HippiusClient:
|
|
431
472
|
Raises:
|
432
473
|
RuntimeError: If deletion fails completely
|
433
474
|
"""
|
434
|
-
return await self.ipfs_client.delete_file(
|
475
|
+
return await self.ipfs_client.delete_file(
|
476
|
+
cid, cancel_from_blockchain, seed_phrase=seed_phrase
|
477
|
+
)
|
435
478
|
|
436
479
|
async def delete_ec_file(
|
437
480
|
self,
|
438
481
|
metadata_cid: str,
|
439
482
|
cancel_from_blockchain: bool = True,
|
440
483
|
parallel_limit: int = 20,
|
484
|
+
seed_phrase: Optional[str] = None,
|
441
485
|
) -> bool:
|
442
486
|
"""
|
443
487
|
Delete an erasure-coded file, including all its chunks in parallel.
|
@@ -446,6 +490,7 @@ class HippiusClient:
|
|
446
490
|
metadata_cid: CID of the metadata file for the erasure-coded file
|
447
491
|
cancel_from_blockchain: Whether to cancel storage from blockchain
|
448
492
|
parallel_limit: Maximum number of concurrent deletion operations
|
493
|
+
seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
|
449
494
|
|
450
495
|
Returns:
|
451
496
|
True or false if failed.
|
@@ -454,5 +499,8 @@ class HippiusClient:
|
|
454
499
|
RuntimeError: If deletion fails completely
|
455
500
|
"""
|
456
501
|
return await self.ipfs_client.delete_ec_file(
|
457
|
-
metadata_cid,
|
502
|
+
metadata_cid,
|
503
|
+
cancel_from_blockchain,
|
504
|
+
parallel_limit,
|
505
|
+
seed_phrase=seed_phrase,
|
458
506
|
)
|
@@ -380,7 +380,7 @@ def decrypt_seed_phrase(
|
|
380
380
|
Decrypt the substrate seed phrase using password-based decryption.
|
381
381
|
|
382
382
|
Args:
|
383
|
-
password: Optional password (if None, will prompt)
|
383
|
+
password: Optional password (if None, will prompt; if empty string, will skip password for read-only operations)
|
384
384
|
account_name: Optional account name (if None, uses active account)
|
385
385
|
|
386
386
|
Returns:
|
@@ -412,6 +412,12 @@ def decrypt_seed_phrase(
|
|
412
412
|
print("Error: No encrypted seed phrase found or missing salt")
|
413
413
|
return None
|
414
414
|
|
415
|
+
# Check if we're in skip-password mode (empty string)
|
416
|
+
# This is used for read-only operations that don't need blockchain interaction
|
417
|
+
if password == "":
|
418
|
+
# Don't print a message as it's confusing to the user
|
419
|
+
return None
|
420
|
+
|
415
421
|
# Get password from user if not provided
|
416
422
|
if password is None:
|
417
423
|
password = getpass.getpass("Enter password to decrypt seed phrase: \n\n")
|
@@ -427,7 +433,8 @@ def get_seed_phrase(
|
|
427
433
|
Get the substrate seed phrase from configuration, decrypting if necessary.
|
428
434
|
|
429
435
|
Args:
|
430
|
-
password: Optional password for decryption (if None and needed, will prompt
|
436
|
+
password: Optional password for decryption (if None and needed, will prompt;
|
437
|
+
if empty string, will skip decryption for read-only operations)
|
431
438
|
account_name: Optional account name (if None, uses active account)
|
432
439
|
|
433
440
|
Returns:
|
@@ -449,6 +456,11 @@ def get_seed_phrase(
|
|
449
456
|
account_data = config["substrate"]["accounts"][name_to_use]
|
450
457
|
is_encoded = account_data.get("seed_phrase_encoded", False)
|
451
458
|
|
459
|
+
# If password is an empty string, this indicates we're doing a read-only operation
|
460
|
+
# that doesn't require the seed phrase, so we can return None
|
461
|
+
if password == "" and is_encoded:
|
462
|
+
return None
|
463
|
+
|
452
464
|
if is_encoded:
|
453
465
|
return decrypt_seed_phrase(password, name_to_use)
|
454
466
|
else:
|