hippius 0.1.7__py3-none-any.whl → 0.1.10__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.1.7.dist-info → hippius-0.1.10.dist-info}/METADATA +189 -9
- hippius-0.1.10.dist-info/RECORD +12 -0
- hippius_sdk/__init__.py +4 -0
- hippius_sdk/account.py +648 -0
- hippius_sdk/cli.py +645 -445
- hippius_sdk/client.py +1 -1
- hippius_sdk/config.py +72 -1
- hippius_sdk/ipfs.py +1 -1
- hippius_sdk/substrate.py +252 -154
- hippius_sdk/utils.py +87 -0
- hippius-0.1.7.dist-info/RECORD +0 -10
- {hippius-0.1.7.dist-info → hippius-0.1.10.dist-info}/WHEEL +0 -0
- {hippius-0.1.7.dist-info → hippius-0.1.10.dist-info}/entry_points.txt +0 -0
hippius_sdk/cli.py
CHANGED
@@ -16,6 +16,8 @@ from typing import Optional, List
|
|
16
16
|
import getpass
|
17
17
|
import concurrent.futures
|
18
18
|
import threading
|
19
|
+
import random
|
20
|
+
import uuid
|
19
21
|
|
20
22
|
# Import SDK components
|
21
23
|
from hippius_sdk import HippiusClient
|
@@ -431,9 +433,12 @@ def handle_credits(client, account_address):
|
|
431
433
|
return 0
|
432
434
|
|
433
435
|
|
434
|
-
def handle_files(client, account_address,
|
435
|
-
"""
|
436
|
-
|
436
|
+
def handle_files(client, account_address, show_all_miners=False):
|
437
|
+
"""
|
438
|
+
Display files stored by a user in a nice format.
|
439
|
+
|
440
|
+
This command only reads data and doesn't require seed phrase decryption.
|
441
|
+
"""
|
437
442
|
try:
|
438
443
|
# Get the account address we're querying
|
439
444
|
if account_address is None:
|
@@ -457,105 +462,89 @@ def handle_files(client, account_address, debug=False, show_all_miners=False):
|
|
457
462
|
)
|
458
463
|
return 1
|
459
464
|
|
460
|
-
|
461
|
-
|
465
|
+
# Get files for the account using the new profile-based method
|
466
|
+
print(f"Retrieving files for account: {account_address}")
|
467
|
+
files = client.substrate_client.get_user_files_from_profile(account_address)
|
462
468
|
|
463
|
-
#
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
truncate_miners=True, # Always truncate long miner IDs
|
468
|
-
max_miners=max_miners, # Use 0 for all or 3 for limited
|
469
|
-
)
|
469
|
+
# Check if any files were found
|
470
|
+
if not files:
|
471
|
+
print(f"No files found for account: {account_address}")
|
472
|
+
return 0
|
470
473
|
|
471
|
-
|
472
|
-
|
473
|
-
print("\n" + "-" * 80)
|
474
|
+
print(f"\nFound {len(files)} files for account: {account_address}")
|
475
|
+
print("-" * 80)
|
474
476
|
|
475
|
-
|
477
|
+
for i, file in enumerate(files, 1):
|
478
|
+
try:
|
476
479
|
print(f"File {i}:")
|
477
480
|
|
478
|
-
#
|
481
|
+
# Display file hash/CID
|
479
482
|
file_hash = file.get("file_hash", "Unknown")
|
480
|
-
|
481
|
-
|
483
|
+
if file_hash is not None:
|
484
|
+
formatted_cid = client.format_cid(file_hash)
|
485
|
+
print(f" CID: {formatted_cid}")
|
486
|
+
else:
|
487
|
+
print(f" CID: Unknown (None)")
|
482
488
|
|
483
489
|
# Display file name
|
484
|
-
|
490
|
+
file_name = file.get("file_name", "Unnamed")
|
491
|
+
print(
|
492
|
+
f" File name: {file_name if file_name is not None else 'Unnamed'}"
|
493
|
+
)
|
485
494
|
|
486
|
-
# Display file size
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
495
|
+
# Display file size
|
496
|
+
if "size_formatted" in file and file["size_formatted"] is not None:
|
497
|
+
size_formatted = file["size_formatted"]
|
498
|
+
file_size = file.get("file_size", 0)
|
499
|
+
if file_size is not None:
|
500
|
+
print(f" File size: {file_size:,} bytes ({size_formatted})")
|
501
|
+
else:
|
502
|
+
print(f" File size: Unknown")
|
503
|
+
else:
|
504
|
+
print(f" File size: Unknown")
|
492
505
|
|
493
|
-
# Display miners
|
506
|
+
# Display miners (if available)
|
507
|
+
miner_ids = file.get("miner_ids", [])
|
494
508
|
miner_count = file.get("miner_count", 0)
|
495
|
-
miners = file.get("miner_ids", [])
|
496
509
|
|
497
|
-
if
|
498
|
-
print(f"
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
print(
|
503
|
-
f" (Showing {len(miners)} of {miner_count} miners - use --all-miners to see all)"
|
510
|
+
if miner_ids and show_all_miners:
|
511
|
+
print(f" Stored by {len(miner_ids)} miners:")
|
512
|
+
for miner in miner_ids:
|
513
|
+
miner_id = (
|
514
|
+
miner.get("id", miner) if isinstance(miner, dict) else miner
|
504
515
|
)
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
print(f" - {miner}")
|
516
|
+
formatted = (
|
517
|
+
miner.get("formatted", miner_id)
|
518
|
+
if isinstance(miner, dict)
|
519
|
+
else miner_id
|
520
|
+
)
|
521
|
+
print(f" - {formatted}")
|
522
|
+
elif miner_count:
|
523
|
+
print(f" Stored by {miner_count} miners")
|
514
524
|
else:
|
515
|
-
print("
|
525
|
+
print(f" Storage information not available")
|
516
526
|
|
517
527
|
print("-" * 80)
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
528
|
+
except Exception as e:
|
529
|
+
print(f" Error displaying file {i}: {e}")
|
530
|
+
print("-" * 80)
|
531
|
+
continue
|
532
|
+
|
533
|
+
# Add tip for downloading
|
534
|
+
if files:
|
535
|
+
print("\nTo download a file, use:")
|
536
|
+
print(f" hippius download <CID> <output_filename>")
|
537
|
+
|
522
538
|
except Exception as e:
|
523
|
-
print(f"Error retrieving
|
539
|
+
print(f"Error retrieving files: {e}")
|
524
540
|
return 1
|
525
541
|
|
526
542
|
return 0
|
527
543
|
|
528
544
|
|
529
545
|
def handle_ec_files(client, account_address, show_all_miners=False, show_chunks=False):
|
530
|
-
"""
|
531
|
-
|
532
|
-
|
533
|
-
This command only reads data and doesn't require seed phrase decryption.
|
534
|
-
"""
|
535
|
-
# For progress reporting
|
536
|
-
processed_files = 0
|
537
|
-
total_files = 0
|
538
|
-
lock = threading.Lock()
|
539
|
-
|
540
|
-
# Store results from worker threads
|
541
|
-
results = {
|
542
|
-
"ec_files": [],
|
543
|
-
"binary_files": 0,
|
544
|
-
"json_decode_errors": 0,
|
545
|
-
"not_ec_files": 0,
|
546
|
-
"skipped_files": 0,
|
547
|
-
}
|
548
|
-
|
549
|
-
# For quick identification of potential EC files - common naming patterns
|
550
|
-
EC_FILENAME_PATTERNS = ["metadata", "ec-", "erasure"]
|
551
|
-
|
552
|
-
# Debug print function that can be enabled/disabled
|
553
|
-
verbose = get_config_value("cli", "verbose", False)
|
554
|
-
|
555
|
-
def debug_print(msg):
|
556
|
-
if verbose:
|
557
|
-
print(msg)
|
558
|
-
|
546
|
+
"""Handle the ec-files command to show only erasure-coded files"""
|
547
|
+
print("Looking for erasure-coded files...")
|
559
548
|
try:
|
560
549
|
# Get the account address we're querying
|
561
550
|
if account_address is None:
|
@@ -579,251 +568,174 @@ def handle_ec_files(client, account_address, show_all_miners=False, show_chunks=
|
|
579
568
|
)
|
580
569
|
return 1
|
581
570
|
|
582
|
-
#
|
583
|
-
|
584
|
-
files = client.substrate_client.get_user_files(
|
585
|
-
account_address=account_address,
|
586
|
-
truncate_miners=not show_all_miners,
|
587
|
-
max_miners=0 if show_all_miners else 3,
|
588
|
-
)
|
589
|
-
|
590
|
-
if not files:
|
591
|
-
print(f"No files found for account: {account_address}")
|
592
|
-
return
|
593
|
-
|
594
|
-
total_files = len(files)
|
595
|
-
print(f"Found {total_files} files, analyzing for erasure-coded metadata...")
|
571
|
+
# First, get all user files using the profile method
|
572
|
+
files = client.substrate_client.get_user_files_from_profile(account_address)
|
596
573
|
|
597
|
-
#
|
598
|
-
|
574
|
+
# Filter for metadata files (ending with .ec_metadata)
|
575
|
+
ec_metadata_files = []
|
599
576
|
for file in files:
|
600
|
-
|
601
|
-
if
|
602
|
-
|
603
|
-
|
577
|
+
file_name = file.get("file_name", "")
|
578
|
+
if (
|
579
|
+
file_name
|
580
|
+
and isinstance(file_name, str)
|
581
|
+
and file_name.endswith(".ec_metadata")
|
582
|
+
):
|
583
|
+
ec_metadata_files.append(file)
|
604
584
|
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
# Higher chance this is an EC file
|
609
|
-
debug_print(f" - Potential EC file based on name: {name}")
|
610
|
-
potential_ec_files.append((0, file)) # Priority 0 = high
|
611
|
-
else:
|
612
|
-
# Still check it, but with lower priority
|
613
|
-
potential_ec_files.append((1, file)) # Priority 1 = lower
|
614
|
-
|
615
|
-
# Sort by priority (check likely EC files first)
|
616
|
-
potential_ec_files.sort(key=lambda x: x[0])
|
617
|
-
priority_files = [f for _, f in potential_ec_files]
|
618
|
-
|
619
|
-
# Progress update function
|
620
|
-
def update_progress():
|
621
|
-
nonlocal processed_files
|
622
|
-
processed_files += 1
|
623
|
-
if processed_files % 5 == 0 or processed_files == total_files:
|
624
|
-
print(
|
625
|
-
f" Progress: {processed_files}/{total_files} files analyzed ({(processed_files/total_files)*100:.1f}%)",
|
626
|
-
end="\r",
|
627
|
-
)
|
585
|
+
if not ec_metadata_files:
|
586
|
+
print(f"No erasure-coded files found for account {account_address}")
|
587
|
+
return 0
|
628
588
|
|
629
|
-
|
630
|
-
|
631
|
-
try:
|
632
|
-
cid = file.get("file_hash")
|
633
|
-
name = file.get("file_name", "")
|
589
|
+
print(f"\nFound {len(ec_metadata_files)} erasure-coded files:")
|
590
|
+
print("-" * 80)
|
634
591
|
|
635
|
-
|
592
|
+
for i, file in enumerate(ec_metadata_files, 1):
|
593
|
+
try:
|
594
|
+
print(f"EC File {i}:")
|
636
595
|
|
637
|
-
#
|
638
|
-
|
639
|
-
if not
|
640
|
-
|
641
|
-
|
642
|
-
update_progress()
|
643
|
-
return None
|
596
|
+
# Get the metadata CID
|
597
|
+
metadata_cid = file.get("file_hash", "Unknown")
|
598
|
+
if metadata_cid is not None and metadata_cid != "Unknown":
|
599
|
+
formatted_cid = client.format_cid(metadata_cid)
|
600
|
+
print(f" Metadata CID: {formatted_cid}")
|
644
601
|
|
645
|
-
|
646
|
-
if isinstance(content, bytes):
|
602
|
+
# Fetch and parse the metadata to get original file info
|
647
603
|
try:
|
648
|
-
#
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
elif (
|
667
|
-
isinstance(metadata_obj, dict)
|
668
|
-
and metadata_obj.get("erasure_coding")
|
669
|
-
and metadata_obj.get("original_file")
|
670
|
-
):
|
671
|
-
# This is the newer format with a different structure
|
672
|
-
metadata_obj = {
|
673
|
-
"original_name": metadata_obj.get(
|
674
|
-
"original_file", {}
|
675
|
-
).get("name", "unknown"),
|
676
|
-
"k": metadata_obj.get("erasure_coding", {}).get(
|
677
|
-
"k"
|
678
|
-
),
|
679
|
-
"m": metadata_obj.get("erasure_coding", {}).get(
|
680
|
-
"m"
|
681
|
-
),
|
682
|
-
"original_size": metadata_obj.get(
|
683
|
-
"original_file", {}
|
684
|
-
).get("size", 0),
|
685
|
-
"encrypted": metadata_obj.get("encrypted", False),
|
686
|
-
"chunks": metadata_obj.get("chunks", []),
|
687
|
-
}
|
688
|
-
is_ec_file = True
|
689
|
-
|
690
|
-
if is_ec_file:
|
691
|
-
# Found an erasure-coded file!
|
692
|
-
debug_print(
|
693
|
-
f" ✓ Found erasure-coded file: {metadata_obj.get('original_name')}"
|
604
|
+
# Use the formatted CID, not the raw hex-encoded version
|
605
|
+
metadata = client.ipfs_client.cat(formatted_cid)
|
606
|
+
|
607
|
+
# Check if we have text content
|
608
|
+
if metadata.get("is_text", False):
|
609
|
+
# Parse the metadata content as JSON
|
610
|
+
import json
|
611
|
+
|
612
|
+
metadata_json = json.loads(metadata.get("content", "{}"))
|
613
|
+
|
614
|
+
# Extract original file info
|
615
|
+
# Check both possible formats
|
616
|
+
original_file = metadata_json.get("original_file", {})
|
617
|
+
|
618
|
+
if original_file:
|
619
|
+
# New format
|
620
|
+
print(
|
621
|
+
f" Original file name: {original_file.get('name', 'Unknown')}"
|
694
622
|
)
|
695
623
|
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
"
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
)
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
624
|
+
# Show file size
|
625
|
+
original_size = original_file.get("size", 0)
|
626
|
+
if original_size:
|
627
|
+
size_formatted = client.format_size(original_size)
|
628
|
+
print(
|
629
|
+
f" Original file size: {original_size:,} bytes ({size_formatted})"
|
630
|
+
)
|
631
|
+
else:
|
632
|
+
print(f" Original file size: Unknown")
|
633
|
+
|
634
|
+
# Show hash/CID of original file if available
|
635
|
+
original_hash = original_file.get("hash", "")
|
636
|
+
if original_hash:
|
637
|
+
print(f" Original file hash: {original_hash}")
|
638
|
+
|
639
|
+
# Show extension if available
|
640
|
+
extension = original_file.get("extension", "")
|
641
|
+
if extension:
|
642
|
+
print(f" File extension: {extension}")
|
643
|
+
else:
|
644
|
+
# Try older format
|
645
|
+
original_name = metadata_json.get(
|
646
|
+
"original_name", "Unknown"
|
647
|
+
)
|
648
|
+
print(f" Original file name: {original_name}")
|
649
|
+
|
650
|
+
original_size = metadata_json.get("original_size", 0)
|
651
|
+
if original_size:
|
652
|
+
size_formatted = client.format_size(original_size)
|
653
|
+
print(
|
654
|
+
f" Original file size: {original_size:,} bytes ({size_formatted})"
|
655
|
+
)
|
656
|
+
else:
|
657
|
+
print(f" Original file size: Unknown")
|
658
|
+
|
659
|
+
# Show erasure coding parameters if available
|
660
|
+
ec_params = metadata_json.get("erasure_coding", {})
|
661
|
+
if ec_params:
|
662
|
+
k = ec_params.get("k", 0)
|
663
|
+
m = ec_params.get("m", 0)
|
664
|
+
if k and m:
|
665
|
+
print(
|
666
|
+
f" Erasure coding: k={k}, m={m} (need {k} of {k+m} parts)"
|
667
|
+
)
|
719
668
|
else:
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
669
|
+
# Check old format
|
670
|
+
k = metadata_json.get("k", 0)
|
671
|
+
m = metadata_json.get("m", 0)
|
672
|
+
if k and m:
|
673
|
+
print(
|
674
|
+
f" Erasure coding: k={k}, m={m} (need {k} of {k+m} parts)"
|
675
|
+
)
|
676
|
+
|
677
|
+
# Show encryption status if available
|
678
|
+
encrypted = metadata_json.get("encrypted", False)
|
679
|
+
print(f" Encrypted: {'Yes' if encrypted else 'No'}")
|
680
|
+
|
681
|
+
# Count chunks
|
682
|
+
chunks = metadata_json.get("chunks", [])
|
683
|
+
if chunks:
|
684
|
+
print(f" Total chunks: {len(chunks)}")
|
685
|
+
|
686
|
+
# Show chunk details if requested
|
687
|
+
if show_chunks:
|
688
|
+
print(f" Chunks:")
|
689
|
+
for j, chunk in enumerate(chunks):
|
690
|
+
chunk_cid = (
|
691
|
+
chunk
|
692
|
+
if isinstance(chunk, str)
|
693
|
+
else chunk.get("cid", "Unknown")
|
694
|
+
)
|
695
|
+
print(f" Chunk {j+1}: {chunk_cid}")
|
696
|
+
else:
|
697
|
+
# Couldn't parse metadata as text
|
698
|
+
print(f" Error: Metadata is not in text format")
|
699
|
+
except Exception as e:
|
700
|
+
print(f" Error fetching metadata: {e}")
|
737
701
|
else:
|
738
|
-
|
739
|
-
results["skipped_files"] += 1
|
740
|
-
update_progress()
|
741
|
-
return None
|
742
|
-
except Exception as e:
|
743
|
-
# For other unexpected errors
|
744
|
-
debug_print(f" ✗ Error processing {name}: {str(e)}")
|
745
|
-
with lock:
|
746
|
-
results["skipped_files"] += 1
|
747
|
-
update_progress()
|
748
|
-
return None
|
749
|
-
|
750
|
-
# Process files in parallel - significantly speeds up execution
|
751
|
-
max_workers = min(10, len(files)) # Don't create too many threads
|
752
|
-
print(f"Processing files using {max_workers} parallel workers...")
|
753
|
-
|
754
|
-
with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
|
755
|
-
# Start processing files - higher priority files will be submitted first
|
756
|
-
futures = []
|
757
|
-
# First process files that look like metadata files
|
758
|
-
for file in priority_files:
|
759
|
-
futures.append(executor.submit(process_file, file))
|
760
|
-
|
761
|
-
# Wait for all processing to complete
|
762
|
-
concurrent.futures.wait(futures)
|
702
|
+
print(f" Metadata CID: Unknown (None)")
|
763
703
|
|
764
|
-
|
765
|
-
|
766
|
-
|
704
|
+
# Display file name (metadata file name)
|
705
|
+
file_name = file.get("file_name", "Unnamed")
|
706
|
+
print(
|
707
|
+
f" Metadata file name: {file_name if file_name is not None else 'Unnamed'}"
|
708
|
+
)
|
767
709
|
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
print(f" JSON parse errors: {results['json_decode_errors']}")
|
775
|
-
print(f" Not erasure-coded: {results['not_ec_files']}")
|
776
|
-
print(f" Other skipped: {results['skipped_files']}")
|
777
|
-
|
778
|
-
# Print results
|
779
|
-
ec_files = results["ec_files"]
|
780
|
-
if not ec_files:
|
781
|
-
print(f"\nNo erasure-coded files found for this account.")
|
782
|
-
return
|
783
|
-
|
784
|
-
print(f"\nFound {len(ec_files)} erasure-coded files:\n")
|
785
|
-
|
786
|
-
for i, file in enumerate(ec_files):
|
787
|
-
print(f"{i+1}. {file['original_name']} ({file['size_formatted']})")
|
788
|
-
print(f" Metadata CID: {file['metadata_cid']}")
|
789
|
-
print(
|
790
|
-
f" Erasure coding: {file['k']}/{file['m']} scheme ({file['total_chunks']} chunks)"
|
791
|
-
)
|
792
|
-
print(f" Encrypted: {'Yes' if file['encrypted'] else 'No'}")
|
793
|
-
print(f" Stored by {file['miner_count']} miners")
|
794
|
-
|
795
|
-
if file.get("miner_ids") and show_all_miners:
|
796
|
-
print("\n Miners:")
|
797
|
-
for miner in file["miner_ids"]:
|
798
|
-
miner_id = (
|
799
|
-
miner.get("id", miner) if isinstance(miner, dict) else miner
|
710
|
+
# Show reconstruction command
|
711
|
+
if metadata_cid is not None and metadata_cid != "Unknown":
|
712
|
+
print(f" Reconstruction command:")
|
713
|
+
# Try to extract original name from metadata file name
|
714
|
+
original_name = (
|
715
|
+
file_name.replace(".ec_metadata", "") if file_name else "file"
|
800
716
|
)
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
else miner_id
|
717
|
+
# We're already using formatted_cid from above
|
718
|
+
print(
|
719
|
+
f" hippius reconstruct {formatted_cid} reconstructed_{original_name}"
|
805
720
|
)
|
806
|
-
|
807
|
-
|
808
|
-
if show_chunks and file.get("chunks"):
|
809
|
-
print("\n Chunks:")
|
810
|
-
for j, chunk in enumerate(file["chunks"]):
|
811
|
-
if isinstance(chunk, dict):
|
812
|
-
print(f" {j+1}. CID: {chunk.get('cid')}")
|
813
|
-
else:
|
814
|
-
print(f" {j+1}. CID: {chunk}")
|
721
|
+
else:
|
722
|
+
print(f" Reconstruction command not available (missing CID)")
|
815
723
|
|
816
|
-
|
724
|
+
print("-" * 80)
|
725
|
+
except Exception as e:
|
726
|
+
print(f" Error displaying EC file {i}: {e}")
|
727
|
+
print("-" * 80)
|
728
|
+
continue
|
817
729
|
|
818
|
-
|
819
|
-
print("
|
730
|
+
# Add helpful tips
|
731
|
+
print("\nTo reconstruct a file, use:")
|
732
|
+
print(f" hippius reconstruct <Metadata_CID> <output_filename>")
|
820
733
|
|
821
734
|
except Exception as e:
|
822
|
-
print(f"Error: {e}")
|
823
|
-
|
824
|
-
import traceback
|
735
|
+
print(f"Error retrieving erasure-coded files: {e}")
|
736
|
+
return 1
|
825
737
|
|
826
|
-
|
738
|
+
return 0
|
827
739
|
|
828
740
|
|
829
741
|
def handle_erasure_code(
|
@@ -977,8 +889,11 @@ def handle_erasure_code(
|
|
977
889
|
print(f" 1. The metadata CID: {metadata_cid}")
|
978
890
|
print(" 2. Access to at least k chunks for each original chunk")
|
979
891
|
print("\nReconstruction command:")
|
892
|
+
|
893
|
+
# Format the CID for the command
|
894
|
+
formatted_cid = client.format_cid(metadata_cid)
|
980
895
|
print(
|
981
|
-
f" hippius reconstruct {
|
896
|
+
f" hippius reconstruct {formatted_cid} reconstructed_{original_file.get('name')}"
|
982
897
|
)
|
983
898
|
|
984
899
|
return 0
|
@@ -1165,9 +1080,12 @@ def handle_reconstruct(client, metadata_cid, output_file, verbose=True):
|
|
1165
1080
|
start_time = time.time()
|
1166
1081
|
|
1167
1082
|
try:
|
1168
|
-
#
|
1083
|
+
# Format the CID to ensure it's properly handled
|
1084
|
+
formatted_cid = client.format_cid(metadata_cid)
|
1085
|
+
|
1086
|
+
# Use the formatted CID for reconstruction
|
1169
1087
|
result = client.reconstruct_from_erasure_code(
|
1170
|
-
metadata_cid=
|
1088
|
+
metadata_cid=formatted_cid, output_file=output_file, verbose=verbose
|
1171
1089
|
)
|
1172
1090
|
|
1173
1091
|
elapsed_time = time.time() - start_time
|
@@ -1439,125 +1357,316 @@ def handle_seed_phrase_status(account_name=None):
|
|
1439
1357
|
return 0
|
1440
1358
|
|
1441
1359
|
|
1442
|
-
def handle_account_list():
|
1443
|
-
"""Handle listing
|
1444
|
-
|
1360
|
+
def handle_account_list(args):
|
1361
|
+
"""Handle listing accounts."""
|
1362
|
+
from hippius_sdk.account import AccountManager
|
1445
1363
|
|
1446
|
-
|
1447
|
-
print("No accounts configured")
|
1448
|
-
return 0
|
1364
|
+
account_manager = AccountManager()
|
1449
1365
|
|
1450
|
-
|
1366
|
+
if args.type == "coldkey":
|
1367
|
+
accounts = account_manager.list_coldkeys()
|
1368
|
+
print(f"Found {len(accounts)} coldkeys:")
|
1451
1369
|
|
1452
|
-
|
1453
|
-
|
1454
|
-
encoded_status = (
|
1455
|
-
"encrypted" if data.get("seed_phrase_encoded", False) else "plain text"
|
1456
|
-
)
|
1457
|
-
address = data.get("ss58_address", "unknown")
|
1370
|
+
for i, account in enumerate(accounts, 1):
|
1371
|
+
print(f"{i}. {account['name']}: {account['address']}")
|
1458
1372
|
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1373
|
+
# If verbose, show associated hotkeys
|
1374
|
+
if args.verbose:
|
1375
|
+
hotkeys = account_manager.list_hotkeys(account["address"])
|
1376
|
+
if hotkeys:
|
1377
|
+
print(f" Associated hotkeys:")
|
1378
|
+
for j, hotkey in enumerate(hotkeys, 1):
|
1379
|
+
print(f" {j}. {hotkey['name']}: {hotkey['address']}")
|
1380
|
+
else:
|
1381
|
+
print(f" No associated hotkeys")
|
1463
1382
|
|
1464
|
-
|
1383
|
+
elif args.type == "hotkey":
|
1384
|
+
try:
|
1385
|
+
if args.coldkey:
|
1386
|
+
accounts = account_manager.list_hotkeys(args.coldkey)
|
1387
|
+
coldkey_name = None
|
1388
|
+
if accounts and "coldkey_name" in accounts[0]:
|
1389
|
+
coldkey_name = accounts[0]["coldkey_name"]
|
1390
|
+
|
1391
|
+
if coldkey_name:
|
1392
|
+
print(
|
1393
|
+
f"Found {len(accounts)} hotkeys for coldkey {args.coldkey} ({coldkey_name}):"
|
1394
|
+
)
|
1395
|
+
else:
|
1396
|
+
print(f"Found {len(accounts)} hotkeys for coldkey {args.coldkey}:")
|
1397
|
+
else:
|
1398
|
+
accounts = account_manager.list_hotkeys()
|
1399
|
+
print(f"Found {len(accounts)} hotkeys:")
|
1400
|
+
|
1401
|
+
for i, account in enumerate(accounts, 1):
|
1402
|
+
coldkey_info = ""
|
1403
|
+
if "associated_coldkey" in account:
|
1404
|
+
if "coldkey_name" in account:
|
1405
|
+
coldkey_info = f" → coldkey: {account['coldkey_name']} ({account['associated_coldkey']})"
|
1406
|
+
else:
|
1407
|
+
coldkey_info = f" → coldkey: {account['associated_coldkey']}"
|
1465
1408
|
|
1409
|
+
print(f"{i}. {account['name']}: {account['address']}{coldkey_info}")
|
1466
1410
|
|
1467
|
-
|
1468
|
-
|
1469
|
-
|
1470
|
-
|
1411
|
+
# If verbose, show more details
|
1412
|
+
if args.verbose:
|
1413
|
+
created_at = account.get("created_at", 0)
|
1414
|
+
if created_at:
|
1415
|
+
from datetime import datetime
|
1471
1416
|
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
1417
|
+
created_time = datetime.fromtimestamp(created_at).strftime(
|
1418
|
+
"%Y-%m-%d %H:%M:%S"
|
1419
|
+
)
|
1420
|
+
print(f" Created: {created_time}")
|
1476
1421
|
|
1477
|
-
|
1478
|
-
|
1479
|
-
|
1422
|
+
# Show whether there is a blockchain proxy relationship
|
1423
|
+
try:
|
1424
|
+
proxies = account_manager.list_proxies(
|
1425
|
+
account.get("associated_coldkey")
|
1426
|
+
)
|
1427
|
+
has_proxy = any(
|
1428
|
+
proxy["hotkey"] == account["address"] for proxy in proxies
|
1429
|
+
)
|
1430
|
+
if has_proxy:
|
1431
|
+
print(f" Blockchain proxy: YES (active on-chain)")
|
1432
|
+
else:
|
1433
|
+
print(f" Blockchain proxy: NO (local association only)")
|
1434
|
+
except Exception:
|
1435
|
+
print(f" Blockchain proxy: Unknown (could not verify)")
|
1480
1436
|
|
1437
|
+
except ValueError as e:
|
1438
|
+
print(f"Error: {str(e)}")
|
1439
|
+
return None
|
1440
|
+
except Exception as e:
|
1441
|
+
print(f"Error listing hotkeys: {str(e)}")
|
1442
|
+
return None
|
1481
1443
|
|
1482
|
-
|
1483
|
-
|
1484
|
-
|
1485
|
-
|
1486
|
-
|
1487
|
-
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1444
|
+
elif args.type == "proxy":
|
1445
|
+
try:
|
1446
|
+
proxies = account_manager.list_proxies(args.coldkey)
|
1447
|
+
print(f"Found {len(proxies)} proxy relationships on the blockchain:")
|
1448
|
+
|
1449
|
+
for i, proxy in enumerate(proxies, 1):
|
1450
|
+
coldkey = proxy["coldkey"]
|
1451
|
+
hotkey = proxy["hotkey"]
|
1452
|
+
|
1453
|
+
# Try to get human-readable names
|
1454
|
+
data = account_manager._load_accounts_data()
|
1455
|
+
coldkey_name = (
|
1456
|
+
data.get("coldkeys", {}).get(coldkey, {}).get("name", "Unknown")
|
1457
|
+
)
|
1458
|
+
hotkey_name = (
|
1459
|
+
data.get("hotkeys", {}).get(hotkey, {}).get("name", "Unknown")
|
1460
|
+
)
|
1491
1461
|
|
1492
|
-
|
1493
|
-
|
1462
|
+
print(f"{i}. {coldkey_name} ({coldkey}) → {hotkey_name} ({hotkey})")
|
1463
|
+
print(f" Type: {proxy['proxy_type']}, Delay: {proxy['delay']}")
|
1464
|
+
except Exception as e:
|
1465
|
+
print(f"Error listing proxies: {str(e)}")
|
1466
|
+
|
1467
|
+
return accounts
|
1468
|
+
|
1469
|
+
|
1470
|
+
def handle_account_proxy_create(args):
|
1471
|
+
"""Handle creating a proxy relationship."""
|
1472
|
+
from hippius_sdk.account import AccountManager
|
1494
1473
|
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1474
|
+
account_manager = AccountManager()
|
1475
|
+
|
1476
|
+
try:
|
1477
|
+
result = account_manager.create_proxy_relationship(
|
1478
|
+
coldkey_address=args.coldkey,
|
1479
|
+
hotkey_address=args.hotkey,
|
1480
|
+
proxy_type=args.proxy_type,
|
1481
|
+
delay=args.delay,
|
1482
|
+
password=args.password, # Pass the password if provided
|
1483
|
+
)
|
1484
|
+
|
1485
|
+
if result.get("success"):
|
1486
|
+
print(f"Successfully created proxy relationship!")
|
1487
|
+
print(f"Coldkey: {result['coldkey']}")
|
1488
|
+
print(f"Hotkey: {result['hotkey']}")
|
1489
|
+
print(f"Type: {result['proxy_type']}")
|
1490
|
+
print(f"Tx Hash: {result['transaction_hash']}")
|
1499
1491
|
else:
|
1500
|
-
print(
|
1492
|
+
print(
|
1493
|
+
f"Failed to create proxy relationship: {result.get('error', 'Unknown error')}"
|
1494
|
+
)
|
1501
1495
|
|
1502
|
-
return
|
1503
|
-
else:
|
1504
|
-
return 1
|
1496
|
+
return result
|
1505
1497
|
|
1498
|
+
except Exception as e:
|
1499
|
+
print(f"Error creating proxy relationship: {str(e)}")
|
1500
|
+
return None
|
1506
1501
|
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1511
|
-
|
1512
|
-
|
1502
|
+
|
1503
|
+
def handle_account_proxy_remove(args):
|
1504
|
+
"""Handle removing a proxy relationship."""
|
1505
|
+
from hippius_sdk.account import AccountManager
|
1506
|
+
|
1507
|
+
account_manager = AccountManager()
|
1508
|
+
|
1509
|
+
try:
|
1510
|
+
result = account_manager.remove_proxy(
|
1511
|
+
coldkey_address=args.coldkey,
|
1512
|
+
hotkey_address=args.hotkey,
|
1513
|
+
password=args.password,
|
1514
|
+
proxy_type=args.proxy_type,
|
1515
|
+
delay=args.delay,
|
1513
1516
|
)
|
1514
|
-
confirm = input("Do you want to continue anyway? (y/N): ")
|
1515
|
-
if confirm.lower() not in ("y", "yes"):
|
1516
|
-
print("Operation cancelled")
|
1517
|
-
return 1
|
1518
1517
|
|
1519
|
-
|
1520
|
-
|
1521
|
-
|
1518
|
+
if result.get("success"):
|
1519
|
+
print(f"Successfully removed proxy relationship!")
|
1520
|
+
print(f"Coldkey: {result['coldkey']}")
|
1521
|
+
print(f"Hotkey: {result['hotkey']}")
|
1522
|
+
print(f"Tx Hash: {result['transaction_hash']}")
|
1523
|
+
else:
|
1524
|
+
print(
|
1525
|
+
f"Failed to remove proxy relationship: {result.get('error', 'Unknown error')}"
|
1526
|
+
)
|
1522
1527
|
|
1523
|
-
|
1524
|
-
print(
|
1525
|
-
"This address will be used for commands like 'files' and 'ec-files' when no address is explicitly provided."
|
1526
|
-
)
|
1527
|
-
return 0
|
1528
|
+
return result
|
1528
1529
|
|
1530
|
+
except Exception as e:
|
1531
|
+
print(f"Error removing proxy relationship: {str(e)}")
|
1532
|
+
return None
|
1529
1533
|
|
1530
|
-
def handle_default_address_get():
|
1531
|
-
"""Handle getting the current default address for read-only operations"""
|
1532
|
-
config = load_config()
|
1533
|
-
address = config["substrate"].get("default_address")
|
1534
1534
|
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1538
|
-
print("No default address set for read-only operations")
|
1539
|
-
print("You can set one with: hippius address set-default <ss58_address>")
|
1535
|
+
def handle_account_coldkey_create(args):
|
1536
|
+
"""Handle creating a new coldkey account."""
|
1537
|
+
from hippius_sdk.account import AccountManager
|
1540
1538
|
|
1541
|
-
|
1539
|
+
account_manager = AccountManager()
|
1542
1540
|
|
1541
|
+
# Determine if we're creating from a mnemonic
|
1542
|
+
mnemonic = None
|
1543
|
+
if args.mnemonic:
|
1544
|
+
mnemonic = args.mnemonic
|
1545
|
+
elif args.generate_mnemonic:
|
1546
|
+
# We'll use the keypair generation which automatically creates a mnemonic
|
1547
|
+
pass
|
1543
1548
|
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
else:
|
1552
|
-
print("No default address was set")
|
1549
|
+
try:
|
1550
|
+
coldkey = account_manager.create_coldkey(
|
1551
|
+
name=args.name if args.name else "hippius_coldkey",
|
1552
|
+
mnemonic=mnemonic,
|
1553
|
+
encrypt=not args.no_encrypt, # Default to encrypt=True unless explicitly disabled
|
1554
|
+
password=None, # Let the method handle prompting for password
|
1555
|
+
)
|
1553
1556
|
|
1554
|
-
|
1557
|
+
# Success message is already printed in the create_coldkey method
|
1558
|
+
# If show_mnemonic was requested and it's not encrypted, show it
|
1559
|
+
if args.show_mnemonic and not coldkey.get("encrypted", True):
|
1560
|
+
print("\nIMPORTANT: Save this mnemonic phrase to recover your account!")
|
1561
|
+
print(f"Mnemonic: {coldkey['mnemonic']}")
|
1562
|
+
print("\nWARNING: Never share this mnemonic with anyone!")
|
1555
1563
|
|
1564
|
+
return coldkey
|
1565
|
+
|
1566
|
+
except Exception as e:
|
1567
|
+
print(f"Error creating coldkey: {str(e)}")
|
1568
|
+
return None
|
1556
1569
|
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1570
|
+
|
1571
|
+
def handle_account_hotkey_create(args):
|
1572
|
+
"""Handle creating a new hotkey account."""
|
1573
|
+
from hippius_sdk.account import AccountManager
|
1574
|
+
|
1575
|
+
account_manager = AccountManager()
|
1576
|
+
|
1577
|
+
try:
|
1578
|
+
# If no coldkey provided, let the AccountManager handle it
|
1579
|
+
# It will use the only coldkey if there's just one, or raise an error
|
1580
|
+
hotkey = account_manager.create_hotkey(
|
1581
|
+
name=args.name, coldkey_address=args.coldkey
|
1582
|
+
)
|
1583
|
+
|
1584
|
+
# Success message is already printed in create_hotkey
|
1585
|
+
|
1586
|
+
# If show_mnemonic was requested, show it
|
1587
|
+
if args.show_mnemonic and hotkey.get("mnemonic"):
|
1588
|
+
print("\nHotkey mnemonic: {0}".format(hotkey["mnemonic"]))
|
1589
|
+
print("Note: This mnemonic is only needed if you want to use this hotkey")
|
1590
|
+
print(" outside of the Hippius SDK. For normal operations, the proxy")
|
1591
|
+
print(" relationship will allow your coldkey to authorize actions.")
|
1592
|
+
|
1593
|
+
return hotkey
|
1594
|
+
|
1595
|
+
except ValueError as e:
|
1596
|
+
# Handle the specific error case of multiple coldkeys
|
1597
|
+
if "multiple coldkeys exist" in str(e):
|
1598
|
+
print(f"Error: {e}")
|
1599
|
+
print("\nPlease specify which coldkey to use with:")
|
1600
|
+
print(
|
1601
|
+
" hippius account hotkey create --coldkey <COLDKEY_ADDRESS> [--name <NAME>]"
|
1602
|
+
)
|
1603
|
+
else:
|
1604
|
+
print(f"Error creating hotkey: {str(e)}")
|
1605
|
+
|
1606
|
+
return None
|
1607
|
+
except Exception as e:
|
1608
|
+
print(f"Error creating hotkey: {str(e)}")
|
1609
|
+
return None
|
1610
|
+
|
1611
|
+
|
1612
|
+
def handle_account_proxy_create(args):
|
1613
|
+
"""Handle creating a proxy relationship."""
|
1614
|
+
from hippius_sdk.account import AccountManager
|
1615
|
+
|
1616
|
+
account_manager = AccountManager()
|
1617
|
+
|
1618
|
+
try:
|
1619
|
+
result = account_manager.create_proxy_relationship(
|
1620
|
+
coldkey_address=args.coldkey,
|
1621
|
+
hotkey_address=args.hotkey,
|
1622
|
+
proxy_type=args.proxy_type,
|
1623
|
+
delay=args.delay,
|
1624
|
+
)
|
1625
|
+
|
1626
|
+
if result.get("success"):
|
1627
|
+
print(f"Successfully created proxy relationship!")
|
1628
|
+
print(f"Coldkey: {result['coldkey']}")
|
1629
|
+
print(f"Hotkey: {result['hotkey']}")
|
1630
|
+
print(f"Type: {result['proxy_type']}")
|
1631
|
+
print(f"Tx Hash: {result['transaction_hash']}")
|
1632
|
+
else:
|
1633
|
+
print(
|
1634
|
+
f"Failed to create proxy relationship: {result.get('error', 'Unknown error')}"
|
1635
|
+
)
|
1636
|
+
|
1637
|
+
return result
|
1638
|
+
|
1639
|
+
except Exception as e:
|
1640
|
+
print(f"Error creating proxy relationship: {str(e)}")
|
1641
|
+
return None
|
1642
|
+
|
1643
|
+
|
1644
|
+
def handle_account_proxy_remove(args):
|
1645
|
+
"""Handle removing a proxy relationship."""
|
1646
|
+
from hippius_sdk.account import AccountManager
|
1647
|
+
|
1648
|
+
account_manager = AccountManager()
|
1649
|
+
|
1650
|
+
try:
|
1651
|
+
result = account_manager.remove_proxy(
|
1652
|
+
coldkey_address=args.coldkey, hotkey_address=args.hotkey
|
1653
|
+
)
|
1654
|
+
|
1655
|
+
if result.get("success"):
|
1656
|
+
print(f"Successfully removed proxy relationship!")
|
1657
|
+
print(f"Coldkey: {result['coldkey']}")
|
1658
|
+
print(f"Hotkey: {result['hotkey']}")
|
1659
|
+
print(f"Tx Hash: {result['transaction_hash']}")
|
1660
|
+
else:
|
1661
|
+
print(
|
1662
|
+
f"Failed to remove proxy relationship: {result.get('error', 'Unknown error')}"
|
1663
|
+
)
|
1664
|
+
|
1665
|
+
return result
|
1666
|
+
|
1667
|
+
except Exception as e:
|
1668
|
+
print(f"Error removing proxy relationship: {str(e)}")
|
1669
|
+
return None
|
1561
1670
|
|
1562
1671
|
|
1563
1672
|
def main():
|
@@ -1608,8 +1717,8 @@ examples:
|
|
1608
1717
|
)
|
1609
1718
|
parser.add_argument(
|
1610
1719
|
"--api-url",
|
1611
|
-
default=get_config_value("ipfs", "api_url", "https://
|
1612
|
-
help="IPFS API URL for uploads (default: from config or https://
|
1720
|
+
default=get_config_value("ipfs", "api_url", "https://store.hippius.network"),
|
1721
|
+
help="IPFS API URL for uploads (default: from config or https://store.hippius.network)",
|
1613
1722
|
)
|
1614
1723
|
parser.add_argument(
|
1615
1724
|
"--local-ipfs",
|
@@ -1719,42 +1828,50 @@ examples:
|
|
1719
1828
|
|
1720
1829
|
# Files command
|
1721
1830
|
files_parser = subparsers.add_parser(
|
1722
|
-
"files", help="View
|
1831
|
+
"files", help="View files stored by you or another account"
|
1723
1832
|
)
|
1724
1833
|
files_parser.add_argument(
|
1725
|
-
"account_address",
|
1726
|
-
|
1727
|
-
default=None,
|
1728
|
-
help="Substrate account address (uses keypair address if not specified)",
|
1729
|
-
)
|
1730
|
-
files_parser.add_argument(
|
1731
|
-
"--debug", action="store_true", help="Show debug information about CID decoding"
|
1834
|
+
"--account_address",
|
1835
|
+
help="Substrate account to view files for (defaults to your keyfile account)",
|
1732
1836
|
)
|
1733
1837
|
files_parser.add_argument(
|
1734
1838
|
"--all-miners",
|
1735
1839
|
action="store_true",
|
1736
|
-
help="Show all miners for each file
|
1840
|
+
help="Show all miners for each file",
|
1841
|
+
)
|
1842
|
+
files_parser.set_defaults(
|
1843
|
+
func=lambda args, client: handle_files(
|
1844
|
+
client,
|
1845
|
+
args.account_address,
|
1846
|
+
show_all_miners=args.all_miners if hasattr(args, "all_miners") else False,
|
1847
|
+
)
|
1737
1848
|
)
|
1738
1849
|
|
1739
1850
|
# Erasure Coded Files command
|
1740
1851
|
ec_files_parser = subparsers.add_parser(
|
1741
|
-
"ec-files", help="
|
1852
|
+
"ec-files", help="View erasure-coded files stored by you or another account"
|
1742
1853
|
)
|
1743
1854
|
ec_files_parser.add_argument(
|
1744
|
-
"account_address",
|
1745
|
-
|
1746
|
-
default=None,
|
1747
|
-
help="Substrate account address (uses keypair address if not specified)",
|
1855
|
+
"--account_address",
|
1856
|
+
help="Substrate account to view erasure-coded files for (defaults to your keyfile account)",
|
1748
1857
|
)
|
1749
1858
|
ec_files_parser.add_argument(
|
1750
1859
|
"--all-miners",
|
1751
1860
|
action="store_true",
|
1752
|
-
help="Show all miners for each file
|
1861
|
+
help="Show all miners for each file",
|
1753
1862
|
)
|
1754
1863
|
ec_files_parser.add_argument(
|
1755
1864
|
"--show-chunks",
|
1756
1865
|
action="store_true",
|
1757
|
-
help="Show
|
1866
|
+
help="Show chunk details for each erasure-coded file",
|
1867
|
+
)
|
1868
|
+
ec_files_parser.set_defaults(
|
1869
|
+
func=lambda args, client: handle_ec_files(
|
1870
|
+
client,
|
1871
|
+
args.account_address,
|
1872
|
+
show_all_miners=args.all_miners if hasattr(args, "all_miners") else False,
|
1873
|
+
show_chunks=args.show_chunks if hasattr(args, "show_chunks") else False,
|
1874
|
+
)
|
1758
1875
|
)
|
1759
1876
|
|
1760
1877
|
# Key generation command
|
@@ -1893,55 +2010,139 @@ examples:
|
|
1893
2010
|
)
|
1894
2011
|
|
1895
2012
|
# Account subcommand
|
1896
|
-
account_parser = subparsers.add_parser("account", help="Manage
|
2013
|
+
account_parser = subparsers.add_parser("account", help="Manage blockchain accounts")
|
1897
2014
|
account_subparsers = account_parser.add_subparsers(
|
1898
|
-
dest="account_action", help="Account
|
2015
|
+
dest="account_action", help="Account management actions"
|
1899
2016
|
)
|
1900
2017
|
|
1901
|
-
#
|
1902
|
-
account_subparsers.add_parser(
|
2018
|
+
# Coldkey subcommands
|
2019
|
+
coldkey_parser = account_subparsers.add_parser(
|
2020
|
+
"coldkey", help="Manage coldkey accounts"
|
2021
|
+
)
|
2022
|
+
coldkey_subparsers = coldkey_parser.add_subparsers(
|
2023
|
+
dest="coldkey_action", help="Coldkey actions"
|
2024
|
+
)
|
2025
|
+
|
2026
|
+
# Create coldkey command
|
2027
|
+
create_coldkey_parser = coldkey_subparsers.add_parser(
|
2028
|
+
"create", help="Create a new coldkey"
|
2029
|
+
)
|
2030
|
+
create_coldkey_parser.add_argument(
|
2031
|
+
"--name", help="Name for the coldkey (default: hippius_coldkey)"
|
2032
|
+
)
|
2033
|
+
mnemonic_group = create_coldkey_parser.add_mutually_exclusive_group()
|
2034
|
+
mnemonic_group.add_argument("--mnemonic", help="Mnemonic seed phrase to use")
|
2035
|
+
mnemonic_group.add_argument(
|
2036
|
+
"--generate-mnemonic",
|
2037
|
+
action="store_true",
|
2038
|
+
help="Generate a new mnemonic (default if no mnemonic provided)",
|
2039
|
+
)
|
2040
|
+
create_coldkey_parser.add_argument(
|
2041
|
+
"--show-mnemonic",
|
2042
|
+
action="store_true",
|
2043
|
+
help="Display the mnemonic after creation (USE WITH CAUTION)",
|
2044
|
+
)
|
2045
|
+
create_coldkey_parser.add_argument(
|
2046
|
+
"--no-encrypt",
|
2047
|
+
action="store_true",
|
2048
|
+
help="Do NOT encrypt the mnemonic with a password (not recommended)",
|
2049
|
+
)
|
2050
|
+
create_coldkey_parser.set_defaults(func=handle_account_coldkey_create)
|
1903
2051
|
|
1904
|
-
#
|
1905
|
-
|
1906
|
-
"
|
2052
|
+
# Hotkey subcommands
|
2053
|
+
hotkey_parser = account_subparsers.add_parser(
|
2054
|
+
"hotkey", help="Manage hotkey accounts"
|
1907
2055
|
)
|
1908
|
-
|
1909
|
-
"
|
2056
|
+
hotkey_subparsers = hotkey_parser.add_subparsers(
|
2057
|
+
dest="hotkey_action", help="Hotkey actions"
|
1910
2058
|
)
|
1911
2059
|
|
1912
|
-
#
|
1913
|
-
|
1914
|
-
"
|
2060
|
+
# Create hotkey command
|
2061
|
+
create_hotkey_parser = hotkey_subparsers.add_parser(
|
2062
|
+
"create", help="Create a new hotkey associated with a coldkey"
|
1915
2063
|
)
|
1916
|
-
|
1917
|
-
"
|
2064
|
+
create_hotkey_parser.add_argument(
|
2065
|
+
"--name", help="Name for the hotkey (default: hippius_hotkey_N)"
|
2066
|
+
)
|
2067
|
+
create_hotkey_parser.add_argument(
|
2068
|
+
"--coldkey",
|
2069
|
+
help="Coldkey address to associate with (required if multiple coldkeys exist)",
|
2070
|
+
)
|
2071
|
+
create_hotkey_parser.add_argument(
|
2072
|
+
"--show-mnemonic",
|
2073
|
+
action="store_true",
|
2074
|
+
help="Display the mnemonic after creation",
|
1918
2075
|
)
|
2076
|
+
create_hotkey_parser.set_defaults(func=handle_account_hotkey_create)
|
1919
2077
|
|
1920
|
-
#
|
1921
|
-
|
1922
|
-
|
2078
|
+
# List accounts command
|
2079
|
+
list_accounts_parser = account_subparsers.add_parser("list", help="List accounts")
|
2080
|
+
list_accounts_parser.add_argument(
|
2081
|
+
"type", choices=["coldkey", "hotkey", "proxy"], help="Type of accounts to list"
|
1923
2082
|
)
|
1924
|
-
|
1925
|
-
|
2083
|
+
list_accounts_parser.add_argument("--coldkey", help="Coldkey address to filter by")
|
2084
|
+
list_accounts_parser.add_argument(
|
2085
|
+
"--verbose", "-v", action="store_true", help="Show more details"
|
1926
2086
|
)
|
2087
|
+
list_accounts_parser.set_defaults(func=handle_account_list)
|
1927
2088
|
|
1928
|
-
#
|
1929
|
-
|
1930
|
-
"
|
2089
|
+
# Proxy subcommands
|
2090
|
+
proxy_parser = account_subparsers.add_parser(
|
2091
|
+
"proxy", help="Manage proxy relationships"
|
1931
2092
|
)
|
1932
|
-
|
1933
|
-
"
|
2093
|
+
proxy_subparsers = proxy_parser.add_subparsers(
|
2094
|
+
dest="proxy_action", help="Proxy actions"
|
1934
2095
|
)
|
1935
2096
|
|
1936
|
-
#
|
1937
|
-
|
1938
|
-
"
|
2097
|
+
# Create proxy command
|
2098
|
+
create_proxy_parser = proxy_subparsers.add_parser(
|
2099
|
+
"create", help="Create a proxy relationship"
|
1939
2100
|
)
|
2101
|
+
create_proxy_parser.add_argument(
|
2102
|
+
"--coldkey", required=True, help="Coldkey address (delegator)"
|
2103
|
+
)
|
2104
|
+
create_proxy_parser.add_argument(
|
2105
|
+
"--hotkey", required=True, help="Hotkey address (delegate)"
|
2106
|
+
)
|
2107
|
+
create_proxy_parser.add_argument(
|
2108
|
+
"--proxy-type", default="NonTransfer", help="Proxy type (default: NonTransfer)"
|
2109
|
+
)
|
2110
|
+
create_proxy_parser.add_argument(
|
2111
|
+
"--delay",
|
2112
|
+
type=int,
|
2113
|
+
default=0,
|
2114
|
+
help="Delay in blocks before proxy becomes active (default: 0)",
|
2115
|
+
)
|
2116
|
+
create_proxy_parser.add_argument(
|
2117
|
+
"--password",
|
2118
|
+
help="Password for the coldkey (will prompt if not provided and needed)",
|
2119
|
+
)
|
2120
|
+
create_proxy_parser.set_defaults(func=handle_account_proxy_create)
|
1940
2121
|
|
1941
|
-
#
|
1942
|
-
|
1943
|
-
"
|
2122
|
+
# Remove proxy command
|
2123
|
+
remove_proxy_parser = proxy_subparsers.add_parser(
|
2124
|
+
"remove", help="Remove a proxy relationship"
|
2125
|
+
)
|
2126
|
+
remove_proxy_parser.add_argument(
|
2127
|
+
"--coldkey", required=True, help="Coldkey address (delegator)"
|
2128
|
+
)
|
2129
|
+
remove_proxy_parser.add_argument(
|
2130
|
+
"--hotkey", required=True, help="Hotkey address (delegate)"
|
2131
|
+
)
|
2132
|
+
remove_proxy_parser.add_argument(
|
2133
|
+
"--password",
|
2134
|
+
help="Password for the coldkey (will prompt if not provided and needed)",
|
2135
|
+
)
|
2136
|
+
remove_proxy_parser.add_argument(
|
2137
|
+
"--proxy-type", default="NonTransfer", help="Proxy type (default: NonTransfer)"
|
2138
|
+
)
|
2139
|
+
remove_proxy_parser.add_argument(
|
2140
|
+
"--delay",
|
2141
|
+
type=int,
|
2142
|
+
default=0,
|
2143
|
+
help="Delay value used when creating the proxy (default: 0)",
|
1944
2144
|
)
|
2145
|
+
remove_proxy_parser.set_defaults(func=handle_account_proxy_remove)
|
1945
2146
|
|
1946
2147
|
args = parser.parse_args()
|
1947
2148
|
|
@@ -2032,7 +2233,6 @@ examples:
|
|
2032
2233
|
return handle_files(
|
2033
2234
|
client,
|
2034
2235
|
args.account_address,
|
2035
|
-
debug=args.debug if hasattr(args, "debug") else False,
|
2036
2236
|
show_all_miners=args.all_miners
|
2037
2237
|
if hasattr(args, "all_miners")
|
2038
2238
|
else False,
|