hippius 0.2.3__py3-none-any.whl → 0.2.4__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.
- {hippius-0.2.3.dist-info → hippius-0.2.4.dist-info}/METADATA +207 -151
- hippius-0.2.4.dist-info/RECORD +16 -0
- hippius_sdk/__init__.py +10 -21
- hippius_sdk/cli.py +7 -1
- hippius_sdk/cli_assets.py +8 -6
- hippius_sdk/cli_handlers.py +540 -137
- hippius_sdk/cli_parser.py +5 -0
- hippius_sdk/cli_rich.py +2 -8
- hippius_sdk/client.py +14 -14
- hippius_sdk/config.py +108 -141
- hippius_sdk/ipfs.py +64 -43
- hippius_sdk/substrate.py +24 -33
- hippius-0.2.3.dist-info/RECORD +0 -16
- {hippius-0.2.3.dist-info → hippius-0.2.4.dist-info}/WHEEL +0 -0
- {hippius-0.2.3.dist-info → hippius-0.2.4.dist-info}/entry_points.txt +0 -0
hippius_sdk/cli_parser.py
CHANGED
@@ -520,6 +520,11 @@ def add_account_commands(subparsers):
|
|
520
520
|
help="Account name to show info for (uses active account if not specified)",
|
521
521
|
)
|
522
522
|
|
523
|
+
# Account login
|
524
|
+
login_account_parser = account_subparsers.add_parser(
|
525
|
+
"login", help="Login with an account address and seed phrase"
|
526
|
+
)
|
527
|
+
|
523
528
|
# Account balance
|
524
529
|
balance_account_parser = account_subparsers.add_parser(
|
525
530
|
"balance", help="Check account balance"
|
hippius_sdk/cli_rich.py
CHANGED
@@ -6,14 +6,8 @@ from typing import Any, Dict, List, Optional, Union
|
|
6
6
|
|
7
7
|
from rich.console import Console
|
8
8
|
from rich.panel import Panel
|
9
|
-
from rich.progress import (
|
10
|
-
|
11
|
-
Progress,
|
12
|
-
SpinnerColumn,
|
13
|
-
TextColumn,
|
14
|
-
TimeElapsedColumn,
|
15
|
-
TimeRemainingColumn,
|
16
|
-
)
|
9
|
+
from rich.progress import (BarColumn, Progress, SpinnerColumn, TextColumn,
|
10
|
+
TimeElapsedColumn, TimeRemainingColumn)
|
17
11
|
from rich.table import Table
|
18
12
|
from rich.text import Text
|
19
13
|
|
hippius_sdk/client.py
CHANGED
@@ -64,8 +64,8 @@ class HippiusClient:
|
|
64
64
|
"substrate", "url", "wss://rpc.hippius.network"
|
65
65
|
)
|
66
66
|
|
67
|
-
|
68
|
-
|
67
|
+
# Don't try to get a seed phrase from the legacy location
|
68
|
+
# The substrate_client will handle getting it from the active account
|
69
69
|
|
70
70
|
if encrypt_by_default is None:
|
71
71
|
encrypt_by_default = get_config_value(
|
@@ -82,18 +82,13 @@ class HippiusClient:
|
|
82
82
|
encrypt_by_default=encrypt_by_default,
|
83
83
|
encryption_key=encryption_key,
|
84
84
|
)
|
85
|
-
|
86
85
|
# Initialize Substrate client
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
)
|
94
|
-
except Exception as e:
|
95
|
-
print(f"Warning: Could not initialize Substrate client: {e}")
|
96
|
-
self.substrate_client = None
|
86
|
+
self.substrate_client = SubstrateClient(
|
87
|
+
url=substrate_url,
|
88
|
+
seed_phrase=substrate_seed_phrase,
|
89
|
+
password=seed_phrase_password,
|
90
|
+
account_name=account_name,
|
91
|
+
)
|
97
92
|
|
98
93
|
async def upload_file(
|
99
94
|
self, file_path: str, encrypt: Optional[bool] = None
|
@@ -375,6 +370,7 @@ class HippiusClient:
|
|
375
370
|
max_retries: int = 3,
|
376
371
|
verbose: bool = True,
|
377
372
|
progress_callback: Optional[Callable[[str, int, int], None]] = None,
|
373
|
+
publish: bool = True,
|
378
374
|
) -> Dict[str, Any]:
|
379
375
|
"""
|
380
376
|
Erasure code a file, upload the chunks to IPFS, and store in the Hippius marketplace.
|
@@ -392,9 +388,12 @@ class HippiusClient:
|
|
392
388
|
verbose: Whether to print progress information
|
393
389
|
progress_callback: Optional callback function for progress updates
|
394
390
|
Function receives (stage_name, current, total)
|
391
|
+
publish: Whether to publish to the blockchain (True) or just perform local
|
392
|
+
erasure coding without publishing (False). When False, no password
|
393
|
+
is needed for seed phrase access.
|
395
394
|
|
396
395
|
Returns:
|
397
|
-
dict: Result including metadata CID and transaction hash
|
396
|
+
dict: Result including metadata CID and transaction hash (if published)
|
398
397
|
|
399
398
|
Raises:
|
400
399
|
ValueError: If parameters are invalid
|
@@ -411,6 +410,7 @@ class HippiusClient:
|
|
411
410
|
max_retries=max_retries,
|
412
411
|
verbose=verbose,
|
413
412
|
progress_callback=progress_callback,
|
413
|
+
publish=publish,
|
414
414
|
)
|
415
415
|
|
416
416
|
async def delete_file(
|
hippius_sdk/config.py
CHANGED
@@ -278,34 +278,30 @@ def decrypt_with_password(encrypted_data: str, salt: str, password: str) -> str:
|
|
278
278
|
Returns:
|
279
279
|
str: Decrypted data
|
280
280
|
"""
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
salt_bytes = base64.b64decode(salt)
|
281
|
+
# Decode the encrypted data and salt
|
282
|
+
encrypted_bytes = base64.b64decode(encrypted_data)
|
283
|
+
salt_bytes = base64.b64decode(salt)
|
285
284
|
|
286
|
-
|
287
|
-
|
285
|
+
# Derive the key from the password and salt
|
286
|
+
key, _ = _derive_key_from_password(password, salt_bytes)
|
288
287
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
# Create a SecretBox with our derived key
|
299
|
-
box = nacl.secret.SecretBox(key)
|
288
|
+
# Verify NaCl is available (imported at the top)
|
289
|
+
try:
|
290
|
+
if not hasattr(nacl, "secret") or not hasattr(nacl, "utils"):
|
291
|
+
raise ImportError("NaCl modules not available")
|
292
|
+
except ImportError:
|
293
|
+
raise ValueError(
|
294
|
+
"PyNaCl is required for decryption. Install it with: pip install pynacl"
|
295
|
+
)
|
300
296
|
|
301
|
-
|
302
|
-
|
297
|
+
# Create a SecretBox with our derived key
|
298
|
+
box = nacl.secret.SecretBox(key)
|
303
299
|
|
304
|
-
|
305
|
-
|
300
|
+
# Decrypt the data
|
301
|
+
decrypted_data = box.decrypt(encrypted_bytes)
|
306
302
|
|
307
|
-
|
308
|
-
|
303
|
+
# Return the decrypted string
|
304
|
+
return decrypted_data.decode("utf-8")
|
309
305
|
|
310
306
|
|
311
307
|
def encrypt_seed_phrase(
|
@@ -317,7 +313,7 @@ def encrypt_seed_phrase(
|
|
317
313
|
Args:
|
318
314
|
seed_phrase: The plain text seed phrase to encrypt
|
319
315
|
password: Optional password (if None, will prompt)
|
320
|
-
account_name: Optional name for the account (if None, uses
|
316
|
+
account_name: Optional name for the account (if None, uses active account)
|
321
317
|
|
322
318
|
Returns:
|
323
319
|
bool: True if encryption and saving was successful, False otherwise
|
@@ -344,30 +340,31 @@ def encrypt_seed_phrase(
|
|
344
340
|
|
345
341
|
config = load_config()
|
346
342
|
|
347
|
-
#
|
348
|
-
if
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
}
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
343
|
+
# Only use multi-account mode
|
344
|
+
# Use active account if no account specified
|
345
|
+
name_to_use = account_name
|
346
|
+
if name_to_use is None:
|
347
|
+
name_to_use = config["substrate"].get("active_account")
|
348
|
+
if not name_to_use:
|
349
|
+
# If no active account, we need a name
|
350
|
+
print("Error: No account name specified and no active account")
|
351
|
+
return False
|
352
|
+
|
353
|
+
# Ensure accounts structure exists
|
354
|
+
if "accounts" not in config["substrate"]:
|
355
|
+
config["substrate"]["accounts"] = {}
|
356
|
+
|
357
|
+
# Store the account data
|
358
|
+
config["substrate"]["accounts"][name_to_use] = {
|
359
|
+
"seed_phrase": encrypted_data,
|
360
|
+
"seed_phrase_encoded": True,
|
361
|
+
"seed_phrase_salt": salt,
|
362
|
+
"ss58_address": ss58_address,
|
363
|
+
}
|
364
|
+
|
365
|
+
# Set as active account if no active account exists
|
366
|
+
if not config["substrate"].get("active_account"):
|
367
|
+
config["substrate"]["active_account"] = name_to_use
|
371
368
|
|
372
369
|
return save_config(config)
|
373
370
|
|
@@ -384,60 +381,43 @@ def decrypt_seed_phrase(
|
|
384
381
|
|
385
382
|
Args:
|
386
383
|
password: Optional password (if None, will prompt)
|
387
|
-
account_name: Optional account name (if None, uses active account
|
384
|
+
account_name: Optional account name (if None, uses active account)
|
388
385
|
|
389
386
|
Returns:
|
390
387
|
Optional[str]: The decrypted seed phrase, or None if decryption failed
|
391
388
|
"""
|
392
|
-
|
393
|
-
config = load_config()
|
394
|
-
|
395
|
-
# Determine if we're using multi-account mode
|
396
|
-
if account_name is not None or config["substrate"].get("active_account"):
|
397
|
-
# Multi-account mode
|
398
|
-
name_to_use = account_name or config["substrate"].get("active_account")
|
399
|
-
|
400
|
-
if not name_to_use:
|
401
|
-
print("Error: No account specified and no active account")
|
402
|
-
return None
|
403
|
-
|
404
|
-
if name_to_use not in config["substrate"].get("accounts", {}):
|
405
|
-
print(f"Error: Account '{name_to_use}' not found")
|
406
|
-
return None
|
407
|
-
|
408
|
-
account_data = config["substrate"]["accounts"][name_to_use]
|
409
|
-
is_encoded = account_data.get("seed_phrase_encoded", False)
|
389
|
+
config = load_config()
|
410
390
|
|
411
|
-
|
412
|
-
|
391
|
+
# Only use the multi-account system
|
392
|
+
name_to_use = account_name or config["substrate"].get("active_account")
|
413
393
|
|
414
|
-
|
415
|
-
|
394
|
+
if not name_to_use:
|
395
|
+
print("Error: No account specified and no active account")
|
396
|
+
return None
|
416
397
|
|
417
|
-
|
418
|
-
|
419
|
-
|
398
|
+
if name_to_use not in config["substrate"].get("accounts", {}):
|
399
|
+
print(f"Error: Account '{name_to_use}' not found")
|
400
|
+
return None
|
420
401
|
|
421
|
-
|
422
|
-
|
402
|
+
account_data = config["substrate"]["accounts"][name_to_use]
|
403
|
+
is_encoded = account_data.get("seed_phrase_encoded", False)
|
423
404
|
|
424
|
-
|
425
|
-
|
405
|
+
if not is_encoded:
|
406
|
+
return account_data.get("seed_phrase")
|
426
407
|
|
427
|
-
|
428
|
-
|
429
|
-
return None
|
408
|
+
encrypted_data = account_data.get("seed_phrase")
|
409
|
+
salt = account_data.get("seed_phrase_salt")
|
430
410
|
|
431
|
-
|
432
|
-
|
433
|
-
|
411
|
+
if not encrypted_data or not salt:
|
412
|
+
print("Error: No encrypted seed phrase found or missing salt")
|
413
|
+
return None
|
434
414
|
|
435
|
-
|
436
|
-
|
415
|
+
# Get password from user if not provided
|
416
|
+
if password is None:
|
417
|
+
password = getpass.getpass("Enter password to decrypt seed phrase: \n\n")
|
437
418
|
|
438
|
-
|
439
|
-
|
440
|
-
return None
|
419
|
+
# Decrypt the seed phrase
|
420
|
+
return decrypt_with_password(encrypted_data, salt, password)
|
441
421
|
|
442
422
|
|
443
423
|
def get_seed_phrase(
|
@@ -448,45 +428,31 @@ def get_seed_phrase(
|
|
448
428
|
|
449
429
|
Args:
|
450
430
|
password: Optional password for decryption (if None and needed, will prompt)
|
451
|
-
account_name: Optional account name (if None, uses active account
|
431
|
+
account_name: Optional account name (if None, uses active account)
|
452
432
|
|
453
433
|
Returns:
|
454
434
|
Optional[str]: The seed phrase, or None if not available
|
455
435
|
"""
|
456
436
|
config = load_config()
|
457
437
|
|
458
|
-
#
|
459
|
-
|
460
|
-
# Multi-account mode
|
461
|
-
name_to_use = account_name or config["substrate"].get("active_account")
|
438
|
+
# Only use the multi-account system
|
439
|
+
name_to_use = account_name or config["substrate"].get("active_account")
|
462
440
|
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
if name_to_use not in config["substrate"].get("accounts", {}):
|
468
|
-
print(f"Error: Account '{name_to_use}' not found")
|
469
|
-
return None
|
441
|
+
if not name_to_use:
|
442
|
+
print("Error: No account specified and no active account")
|
443
|
+
return None
|
470
444
|
|
471
|
-
|
472
|
-
|
445
|
+
if name_to_use not in config["substrate"].get("accounts", {}):
|
446
|
+
print(f"Error: Account '{name_to_use}' not found")
|
447
|
+
return None
|
473
448
|
|
474
|
-
|
475
|
-
|
476
|
-
is_encoded = config["substrate"].get("seed_phrase_encoded", False)
|
449
|
+
account_data = config["substrate"]["accounts"][name_to_use]
|
450
|
+
is_encoded = account_data.get("seed_phrase_encoded", False)
|
477
451
|
|
478
452
|
if is_encoded:
|
479
|
-
|
480
|
-
return decrypt_seed_phrase(password, account_name)
|
453
|
+
return decrypt_seed_phrase(password, name_to_use)
|
481
454
|
else:
|
482
|
-
|
483
|
-
if account_name is not None or config["substrate"].get("active_account"):
|
484
|
-
# Multi-account mode
|
485
|
-
name_to_use = account_name or config["substrate"].get("active_account")
|
486
|
-
return config["substrate"]["accounts"][name_to_use].get("seed_phrase")
|
487
|
-
else:
|
488
|
-
# Legacy mode
|
489
|
-
return config["substrate"].get("seed_phrase")
|
455
|
+
return account_data.get("seed_phrase")
|
490
456
|
|
491
457
|
|
492
458
|
def set_seed_phrase(
|
@@ -502,7 +468,7 @@ def set_seed_phrase(
|
|
502
468
|
seed_phrase: The seed phrase to store
|
503
469
|
encode: Whether to encrypt the seed phrase (requires password)
|
504
470
|
password: Optional password for encryption (if None and encode=True, will prompt)
|
505
|
-
account_name: Optional name for the account (if None, uses
|
471
|
+
account_name: Optional name for the account (if None, uses active account)
|
506
472
|
|
507
473
|
Returns:
|
508
474
|
bool: True if saving was successful, False otherwise
|
@@ -520,30 +486,31 @@ def set_seed_phrase(
|
|
520
486
|
except Exception as e:
|
521
487
|
print(f"Warning: Could not derive SS58 address: {e}")
|
522
488
|
|
523
|
-
#
|
524
|
-
if
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
}
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
489
|
+
# Only use multi-account mode
|
490
|
+
# Use active account if no account specified
|
491
|
+
name_to_use = account_name
|
492
|
+
if name_to_use is None:
|
493
|
+
name_to_use = config["substrate"].get("active_account")
|
494
|
+
if not name_to_use:
|
495
|
+
# If no active account, we need a name
|
496
|
+
print("Error: No account name specified and no active account")
|
497
|
+
return False
|
498
|
+
|
499
|
+
# Ensure accounts structure exists
|
500
|
+
if "accounts" not in config["substrate"]:
|
501
|
+
config["substrate"]["accounts"] = {}
|
502
|
+
|
503
|
+
# Store the account data
|
504
|
+
config["substrate"]["accounts"][name_to_use] = {
|
505
|
+
"seed_phrase": seed_phrase,
|
506
|
+
"seed_phrase_encoded": False,
|
507
|
+
"seed_phrase_salt": None,
|
508
|
+
"ss58_address": ss58_address,
|
509
|
+
}
|
510
|
+
|
511
|
+
# Set as active account if no active account exists
|
512
|
+
if not config["substrate"].get("active_account"):
|
513
|
+
config["substrate"]["active_account"] = name_to_use
|
547
514
|
|
548
515
|
return save_config(config)
|
549
516
|
|
hippius_sdk/ipfs.py
CHANGED
@@ -816,6 +816,9 @@ class IPFSClient:
|
|
816
816
|
file_data = self.encrypt_data(file_data)
|
817
817
|
|
818
818
|
# Step 2: Split the file into chunks for erasure coding
|
819
|
+
chunk_size = int(chunk_size)
|
820
|
+
chunk_size = max(1, chunk_size) # Ensure it's at least 1 byte
|
821
|
+
|
819
822
|
chunks = []
|
820
823
|
chunk_positions = []
|
821
824
|
for i in range(0, len(file_data), chunk_size):
|
@@ -825,7 +828,7 @@ class IPFSClient:
|
|
825
828
|
|
826
829
|
# Pad the last chunk if necessary
|
827
830
|
if chunks and len(chunks[-1]) < chunk_size:
|
828
|
-
pad_size = chunk_size - len(chunks[-1])
|
831
|
+
pad_size = int(chunk_size - len(chunks[-1]))
|
829
832
|
chunks[-1] = chunks[-1] + b"\0" * pad_size
|
830
833
|
|
831
834
|
# If we don't have enough chunks for the requested parameters, adjust
|
@@ -1386,6 +1389,7 @@ class IPFSClient:
|
|
1386
1389
|
max_retries: int = 3,
|
1387
1390
|
verbose: bool = True,
|
1388
1391
|
progress_callback: Optional[Callable[[str, int, int], None]] = None,
|
1392
|
+
publish: bool = True,
|
1389
1393
|
) -> Dict[str, Any]:
|
1390
1394
|
"""
|
1391
1395
|
Erasure code a file, upload the chunks to IPFS, and store in the Hippius marketplace.
|
@@ -1404,15 +1408,21 @@ class IPFSClient:
|
|
1404
1408
|
verbose: Whether to print progress information
|
1405
1409
|
progress_callback: Optional callback function for progress updates
|
1406
1410
|
Function receives (stage_name, current, total)
|
1411
|
+
publish: Whether to publish to the blockchain (True) or just perform local
|
1412
|
+
erasure coding without publishing (False). When False, no password
|
1413
|
+
is needed for seed phrase access.
|
1407
1414
|
|
1408
1415
|
Returns:
|
1409
|
-
dict: Result including metadata CID and transaction hash
|
1416
|
+
dict: Result including metadata CID and transaction hash (if published)
|
1410
1417
|
|
1411
1418
|
Raises:
|
1412
1419
|
ValueError: If parameters are invalid
|
1413
1420
|
RuntimeError: If processing fails
|
1414
1421
|
"""
|
1415
|
-
# Step 1:
|
1422
|
+
# Step 1: Create substrate client if we need it and are publishing
|
1423
|
+
if substrate_client is None and publish:
|
1424
|
+
substrate_client = SubstrateClient()
|
1425
|
+
# Step 2: Erasure code the file and upload chunks
|
1416
1426
|
metadata = await self.erasure_code_file(
|
1417
1427
|
file_path=file_path,
|
1418
1428
|
k=k,
|
@@ -1424,50 +1434,52 @@ class IPFSClient:
|
|
1424
1434
|
progress_callback=progress_callback,
|
1425
1435
|
)
|
1426
1436
|
|
1427
|
-
# Step 2: Create substrate client if we need it
|
1428
|
-
if substrate_client is None:
|
1429
|
-
substrate_client = SubstrateClient()
|
1430
|
-
|
1431
1437
|
original_file = metadata["original_file"]
|
1432
1438
|
metadata_cid = metadata["metadata_cid"]
|
1433
1439
|
|
1434
|
-
#
|
1435
|
-
|
1440
|
+
# Initialize transaction hash variable
|
1441
|
+
tx_hash = None
|
1436
1442
|
|
1437
|
-
#
|
1438
|
-
if
|
1439
|
-
|
1440
|
-
|
1441
|
-
)
|
1443
|
+
# Only proceed with blockchain storage if publish is True
|
1444
|
+
if publish:
|
1445
|
+
# Create a list to hold all the file inputs (metadata + all chunks)
|
1446
|
+
all_file_inputs = []
|
1442
1447
|
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1447
|
-
|
1448
|
+
# Step 3: Prepare metadata file for storage
|
1449
|
+
if verbose:
|
1450
|
+
print(
|
1451
|
+
f"Preparing to store metadata and {len(metadata['chunks'])} chunks in the Hippius marketplace..."
|
1452
|
+
)
|
1448
1453
|
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
for i, chunk in enumerate(metadata["chunks"]):
|
1454
|
-
# Extract the CID string from the chunk's cid dictionary
|
1455
|
-
chunk_cid = (
|
1456
|
-
chunk["cid"]["cid"]
|
1457
|
-
if isinstance(chunk["cid"], dict) and "cid" in chunk["cid"]
|
1458
|
-
else chunk["cid"]
|
1454
|
+
# Create a file input for the metadata file
|
1455
|
+
metadata_file_input = FileInput(
|
1456
|
+
file_hash=metadata_cid, file_name=f"{original_file['name']}.ec_metadata"
|
1459
1457
|
)
|
1460
|
-
|
1461
|
-
all_file_inputs.append(chunk_file_input)
|
1458
|
+
all_file_inputs.append(metadata_file_input)
|
1462
1459
|
|
1463
|
-
#
|
1464
|
-
if verbose
|
1465
|
-
print(
|
1466
|
-
|
1460
|
+
# Step 4: Add all chunks to the storage request
|
1461
|
+
if verbose:
|
1462
|
+
print("Adding all chunks to storage request...")
|
1463
|
+
|
1464
|
+
for i, chunk in enumerate(metadata["chunks"]):
|
1465
|
+
# Extract the CID string from the chunk's cid dictionary
|
1466
|
+
chunk_cid = (
|
1467
|
+
chunk["cid"]["cid"]
|
1468
|
+
if isinstance(chunk["cid"], dict) and "cid" in chunk["cid"]
|
1469
|
+
else chunk["cid"]
|
1470
|
+
)
|
1471
|
+
chunk_file_input = FileInput(
|
1472
|
+
file_hash=chunk_cid, file_name=chunk["name"]
|
1467
1473
|
)
|
1474
|
+
all_file_inputs.append(chunk_file_input)
|
1468
1475
|
|
1469
|
-
|
1470
|
-
|
1476
|
+
# Print progress for large numbers of chunks
|
1477
|
+
if verbose and (i + 1) % 50 == 0:
|
1478
|
+
print(
|
1479
|
+
f" Prepared {i + 1}/{len(metadata['chunks'])} chunks for storage"
|
1480
|
+
)
|
1481
|
+
|
1482
|
+
# Step 5: Submit the storage request for all files
|
1471
1483
|
if verbose:
|
1472
1484
|
print(
|
1473
1485
|
f"Submitting storage request for 1 metadata file and {len(metadata['chunks'])} chunks..."
|
@@ -1476,7 +1488,6 @@ class IPFSClient:
|
|
1476
1488
|
tx_hash = await substrate_client.storage_request(
|
1477
1489
|
files=all_file_inputs, miner_ids=miner_ids
|
1478
1490
|
)
|
1479
|
-
|
1480
1491
|
if verbose:
|
1481
1492
|
print("Successfully stored all files in marketplace!")
|
1482
1493
|
print(f"Transaction hash: {tx_hash}")
|
@@ -1485,17 +1496,27 @@ class IPFSClient:
|
|
1485
1496
|
f"Total files stored: {len(all_file_inputs)} (1 metadata + {len(metadata['chunks'])} chunks)"
|
1486
1497
|
)
|
1487
1498
|
|
1488
|
-
|
1499
|
+
result = {
|
1489
1500
|
"metadata": metadata,
|
1490
1501
|
"metadata_cid": metadata_cid,
|
1491
1502
|
"transaction_hash": tx_hash,
|
1492
1503
|
"total_files_stored": len(all_file_inputs),
|
1493
1504
|
}
|
1505
|
+
else:
|
1506
|
+
# Not publishing to blockchain (--no-publish flag used)
|
1507
|
+
if verbose:
|
1508
|
+
print("Not publishing to blockchain (--no-publish flag used)")
|
1509
|
+
print(f"Metadata CID: {metadata_cid}")
|
1510
|
+
print(f"Total chunks: {len(metadata['chunks'])}")
|
1494
1511
|
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1512
|
+
result = {
|
1513
|
+
"metadata": metadata,
|
1514
|
+
"metadata_cid": metadata_cid,
|
1515
|
+
"total_files_stored": len(metadata["chunks"])
|
1516
|
+
+ 1, # +1 for metadata file
|
1517
|
+
}
|
1518
|
+
|
1519
|
+
return result
|
1499
1520
|
|
1500
1521
|
async def delete_file(
|
1501
1522
|
self, cid: str, cancel_from_blockchain: bool = True
|