hippius 0.2.6__tar.gz → 0.2.8__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.6 → hippius-0.2.8}/PKG-INFO +1 -1
- {hippius-0.2.6 → hippius-0.2.8}/hippius_sdk/__init__.py +1 -1
- {hippius-0.2.6 → hippius-0.2.8}/hippius_sdk/cli.py +9 -0
- {hippius-0.2.6 → hippius-0.2.8}/hippius_sdk/cli_handlers.py +191 -33
- {hippius-0.2.6 → hippius-0.2.8}/hippius_sdk/cli_parser.py +26 -3
- {hippius-0.2.6 → hippius-0.2.8}/pyproject.toml +1 -1
- {hippius-0.2.6 → hippius-0.2.8}/README.md +0 -0
- {hippius-0.2.6 → hippius-0.2.8}/hippius_sdk/cli_assets.py +0 -0
- {hippius-0.2.6 → hippius-0.2.8}/hippius_sdk/cli_rich.py +0 -0
- {hippius-0.2.6 → hippius-0.2.8}/hippius_sdk/client.py +0 -0
- {hippius-0.2.6 → hippius-0.2.8}/hippius_sdk/config.py +0 -0
- {hippius-0.2.6 → hippius-0.2.8}/hippius_sdk/errors.py +0 -0
- {hippius-0.2.6 → hippius-0.2.8}/hippius_sdk/ipfs.py +0 -0
- {hippius-0.2.6 → hippius-0.2.8}/hippius_sdk/ipfs_core.py +0 -0
- {hippius-0.2.6 → hippius-0.2.8}/hippius_sdk/substrate.py +0 -0
- {hippius-0.2.6 → hippius-0.2.8}/hippius_sdk/utils.py +0 -0
@@ -236,6 +236,15 @@ def main():
|
|
236
236
|
force=args.force if hasattr(args, "force") else False,
|
237
237
|
)
|
238
238
|
|
239
|
+
elif args.command == "pin":
|
240
|
+
return run_async_handler(
|
241
|
+
cli_handlers.handle_pin,
|
242
|
+
client,
|
243
|
+
args.cid,
|
244
|
+
publish=not args.no_publish if hasattr(args, "no_publish") else True,
|
245
|
+
miner_ids=miner_ids,
|
246
|
+
)
|
247
|
+
|
239
248
|
elif args.command == "ec-delete":
|
240
249
|
return run_async_handler(
|
241
250
|
cli_handlers.handle_ec_delete,
|
@@ -371,20 +371,22 @@ async def handle_store(
|
|
371
371
|
"gateway_url"
|
372
372
|
] = f"{client.ipfs_client.gateway}/ipfs/{result['cid']}"
|
373
373
|
|
374
|
-
# Store on blockchain
|
375
|
-
|
376
|
-
|
377
|
-
file_input = FileInput(
|
378
|
-
file_hash=result["cid"], file_name=file_name
|
379
|
-
)
|
374
|
+
# Store on blockchain - miners are optional
|
375
|
+
# Create a file input for blockchain storage
|
376
|
+
file_input = FileInput(file_hash=result["cid"], file_name=file_name)
|
380
377
|
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
378
|
+
# Submit storage request
|
379
|
+
tx_hash = await client.substrate_client.storage_request(
|
380
|
+
files=[file_input], miner_ids=miner_id_list
|
381
|
+
)
|
385
382
|
|
386
|
-
|
387
|
-
|
383
|
+
# Add transaction hash to result
|
384
|
+
result["transaction_hash"] = tx_hash
|
385
|
+
|
386
|
+
# Add a note about the pinning status command
|
387
|
+
log(
|
388
|
+
"\n[bold yellow]Note:[/bold yellow] The pinning-status command will show a different CID (metadata) rather than the direct file CID."
|
389
|
+
)
|
388
390
|
except Exception as e:
|
389
391
|
warning(f"Failed to publish file globally: {str(e)}")
|
390
392
|
|
@@ -575,12 +577,8 @@ async def handle_store_dir(
|
|
575
577
|
f"Failed to publish file {file_info['name']} globally: {str(e)}"
|
576
578
|
)
|
577
579
|
|
578
|
-
# Store on blockchain if
|
579
|
-
if (
|
580
|
-
miner_ids
|
581
|
-
and hasattr(client, "substrate_client")
|
582
|
-
and client.substrate_client
|
583
|
-
):
|
580
|
+
# Store on blockchain if client is available - miners are optional
|
581
|
+
if hasattr(client, "substrate_client") and client.substrate_client:
|
584
582
|
# Create a file input for blockchain storage
|
585
583
|
file_input = FileInput(
|
586
584
|
file_hash=result["cid"],
|
@@ -595,6 +593,11 @@ async def handle_store_dir(
|
|
595
593
|
# Add transaction hash to result
|
596
594
|
result["transaction_hash"] = tx_hash
|
597
595
|
|
596
|
+
# Add a note about the pinning status command
|
597
|
+
log(
|
598
|
+
"\n[bold yellow]Note:[/bold yellow] The pinning-status command will show a different CID (metadata) rather than the direct directory CID."
|
599
|
+
)
|
600
|
+
|
598
601
|
except Exception as e:
|
599
602
|
warning(f"Failed to publish directory globally: {str(e)}")
|
600
603
|
|
@@ -739,20 +742,32 @@ async def handle_files(
|
|
739
742
|
"""Handle the files command"""
|
740
743
|
# Get the account address we're querying
|
741
744
|
if account_address is None:
|
742
|
-
# If no address provided,
|
745
|
+
# If no address provided, try these options in order:
|
746
|
+
# 1. Keypair from the client (if available)
|
747
|
+
# 2. Address from active account
|
748
|
+
# 3. Default address from config
|
749
|
+
|
750
|
+
# Option 1: Try keypair from client
|
743
751
|
if (
|
744
752
|
hasattr(client.substrate_client, "_keypair")
|
745
753
|
and client.substrate_client._keypair is not None
|
746
754
|
):
|
747
755
|
account_address = client.substrate_client._keypair.ss58_address
|
748
756
|
else:
|
749
|
-
# Try to get
|
750
|
-
|
751
|
-
if
|
752
|
-
account_address =
|
753
|
-
else:
|
754
|
-
has_default = get_default_address() is not None
|
757
|
+
# Option 2: Try to get address from active account
|
758
|
+
active_account = get_active_account()
|
759
|
+
if active_account:
|
760
|
+
account_address = get_account_address(active_account)
|
755
761
|
|
762
|
+
# Option 3: If still not found, try default address
|
763
|
+
if not account_address:
|
764
|
+
default_address = get_default_address()
|
765
|
+
if default_address:
|
766
|
+
account_address = default_address
|
767
|
+
|
768
|
+
# If we still don't have an address, show error
|
769
|
+
if not account_address:
|
770
|
+
has_default = get_default_address() is not None
|
756
771
|
error("No account address provided, and client has no keypair.")
|
757
772
|
|
758
773
|
if has_default:
|
@@ -842,7 +857,7 @@ async def handle_pinning_status(
|
|
842
857
|
cid = pin.get("cid")
|
843
858
|
|
844
859
|
# Display pin information
|
845
|
-
log(f"\n{i}. CID: [bold]{cid}[/bold]")
|
860
|
+
log(f"\n{i}. Metadata CID: [bold]{cid}[/bold]")
|
846
861
|
log(f" File Name: {pin['file_name']}")
|
847
862
|
status = "Assigned" if pin["is_assigned"] else "Pending"
|
848
863
|
log(f" Status: {status}")
|
@@ -861,17 +876,49 @@ async def handle_pinning_status(
|
|
861
876
|
|
862
877
|
# Show content info if requested
|
863
878
|
if show_contents:
|
879
|
+
# Add gateway URL
|
880
|
+
gateway_url = f"{client.ipfs_client.gateway}/ipfs/{cid}"
|
881
|
+
log(f" Gateway URL: {gateway_url}")
|
882
|
+
|
883
|
+
# Try to decode the metadata file to get the original file CID
|
864
884
|
try:
|
865
|
-
|
885
|
+
# Fetch the content
|
886
|
+
cat_result = await client.ipfs_client.cat(cid)
|
866
887
|
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
888
|
+
# Try to parse as JSON
|
889
|
+
try:
|
890
|
+
# Decode JSON from content
|
891
|
+
metadata_json = json.loads(
|
892
|
+
cat_result["content"].decode("utf-8")
|
893
|
+
)
|
894
|
+
|
895
|
+
# This should be an array with one or more file entries
|
896
|
+
if (
|
897
|
+
isinstance(metadata_json, list)
|
898
|
+
and len(metadata_json) > 0
|
899
|
+
):
|
900
|
+
log(f"\n [bold cyan]Contained files:[/bold cyan]")
|
901
|
+
for idx, file_entry in enumerate(metadata_json, 1):
|
902
|
+
if isinstance(file_entry, dict):
|
903
|
+
original_cid = file_entry.get("cid")
|
904
|
+
original_name = file_entry.get("filename")
|
905
|
+
if original_cid:
|
906
|
+
log(
|
907
|
+
f" {idx}. Original CID: [bold green]{original_cid}[/bold green]"
|
908
|
+
)
|
909
|
+
if original_name:
|
910
|
+
log(f" Name: {original_name}")
|
911
|
+
log(
|
912
|
+
f" Gateway URL: {client.ipfs_client.gateway}/ipfs/{original_cid}"
|
913
|
+
)
|
914
|
+
except json.JSONDecodeError:
|
915
|
+
if verbose:
|
916
|
+
log(
|
917
|
+
" [yellow]Could not parse metadata as JSON[/yellow]"
|
918
|
+
)
|
872
919
|
except Exception as e:
|
873
920
|
if verbose:
|
874
|
-
warning(f" Error getting
|
921
|
+
warning(f" Error getting original file CIDs: {e}")
|
875
922
|
except Exception as e:
|
876
923
|
warning(f"Error processing pin {i}: {e}")
|
877
924
|
if verbose:
|
@@ -1713,6 +1760,117 @@ async def handle_delete(client: HippiusClient, cid: str, force: bool = False) ->
|
|
1713
1760
|
return 0
|
1714
1761
|
|
1715
1762
|
|
1763
|
+
async def handle_pin(
|
1764
|
+
client: HippiusClient, cid: str, publish: bool = True, miner_ids=None
|
1765
|
+
) -> int:
|
1766
|
+
"""Handle the pin command to pin a CID to IPFS and optionally publish to blockchain"""
|
1767
|
+
from rich.panel import Panel
|
1768
|
+
|
1769
|
+
# First check if this CID exists
|
1770
|
+
try:
|
1771
|
+
exists_result = await client.exists(cid)
|
1772
|
+
if not exists_result["exists"]:
|
1773
|
+
error(f"CID [bold cyan]{cid}[/bold cyan] not found on IPFS")
|
1774
|
+
return 1
|
1775
|
+
except Exception as e:
|
1776
|
+
warning(f"Error checking if CID exists: {e}")
|
1777
|
+
return 1
|
1778
|
+
|
1779
|
+
# Create operation title based on publish flag
|
1780
|
+
if publish:
|
1781
|
+
info(
|
1782
|
+
f"Preparing to pin and publish content with CID: [bold cyan]{cid}[/bold cyan]"
|
1783
|
+
)
|
1784
|
+
operation_title = "Pin & Publish Operation"
|
1785
|
+
else:
|
1786
|
+
info(f"Preparing to pin content with CID: [bold cyan]{cid}[/bold cyan]")
|
1787
|
+
operation_title = "Pin Operation"
|
1788
|
+
|
1789
|
+
# Display operation details
|
1790
|
+
operation_details = [
|
1791
|
+
f"CID: [bold cyan]{cid}[/bold cyan]",
|
1792
|
+
f"Publishing to blockchain: {'Enabled' if publish else 'Disabled'}",
|
1793
|
+
]
|
1794
|
+
print_panel("\n".join(operation_details), title=operation_title)
|
1795
|
+
|
1796
|
+
# Need to authenticate if publishing to blockchain
|
1797
|
+
if publish:
|
1798
|
+
try:
|
1799
|
+
# Ensure we have a keypair for substrate operations
|
1800
|
+
_ = client.substrate_client._ensure_keypair()
|
1801
|
+
except Exception as e:
|
1802
|
+
warning(f"Failed to initialize blockchain client: {str(e)}")
|
1803
|
+
warning("Will continue with pinning but blockchain publishing may fail")
|
1804
|
+
|
1805
|
+
# Show spinner during pinning
|
1806
|
+
with console.status(
|
1807
|
+
"[cyan]Pinning content to IPFS...[/cyan]", spinner="dots"
|
1808
|
+
) as status:
|
1809
|
+
try:
|
1810
|
+
# Pin the content to IPFS
|
1811
|
+
pin_result = await client.ipfs_client.pin(cid)
|
1812
|
+
|
1813
|
+
if not pin_result.get("success", False):
|
1814
|
+
error(
|
1815
|
+
f"Failed to pin content: {pin_result.get('message', 'Unknown error')}"
|
1816
|
+
)
|
1817
|
+
return 1
|
1818
|
+
|
1819
|
+
# If publishing to blockchain, do that now
|
1820
|
+
if publish:
|
1821
|
+
status.update("[cyan]Publishing content to blockchain...[/cyan]")
|
1822
|
+
|
1823
|
+
# Create a FileInput object for the substrate client
|
1824
|
+
from hippius_sdk.substrate import FileInput
|
1825
|
+
|
1826
|
+
file_input = FileInput(file_hash=cid, file_name=f"pinned_{cid}")
|
1827
|
+
|
1828
|
+
# Submit the storage request
|
1829
|
+
tx_hash = await client.substrate_client.storage_request(
|
1830
|
+
files=[file_input], miner_ids=miner_ids
|
1831
|
+
)
|
1832
|
+
|
1833
|
+
# Create result panel with blockchain details
|
1834
|
+
gateway_url = f"{client.ipfs_client.gateway}/ipfs/{cid}"
|
1835
|
+
panel_details = [
|
1836
|
+
f"Successfully pinned and published: [bold cyan]{cid}[/bold cyan]",
|
1837
|
+
f"Gateway URL: [bold cyan]{gateway_url}[/bold cyan]",
|
1838
|
+
f"Transaction hash: [bold green]{tx_hash}[/bold green]",
|
1839
|
+
"\nThis content is now:",
|
1840
|
+
"1. Pinned to your IPFS node",
|
1841
|
+
"2. Published to the IPFS network",
|
1842
|
+
"3. Stored on the Hippius blockchain",
|
1843
|
+
]
|
1844
|
+
console.print(
|
1845
|
+
Panel(
|
1846
|
+
"\n".join(panel_details),
|
1847
|
+
title="Operation Complete",
|
1848
|
+
border_style="green",
|
1849
|
+
)
|
1850
|
+
)
|
1851
|
+
else:
|
1852
|
+
# Just pinning, no blockchain publishing
|
1853
|
+
gateway_url = f"{client.ipfs_client.gateway}/ipfs/{cid}"
|
1854
|
+
panel_details = [
|
1855
|
+
f"Successfully pinned: [bold cyan]{cid}[/bold cyan]",
|
1856
|
+
f"Gateway URL: [bold cyan]{gateway_url}[/bold cyan]",
|
1857
|
+
"\nThis content is now pinned to your IPFS node.",
|
1858
|
+
"It will remain available as long as your node is running.",
|
1859
|
+
]
|
1860
|
+
console.print(
|
1861
|
+
Panel(
|
1862
|
+
"\n".join(panel_details),
|
1863
|
+
title="Pinning Complete",
|
1864
|
+
border_style="green",
|
1865
|
+
)
|
1866
|
+
)
|
1867
|
+
|
1868
|
+
return 0
|
1869
|
+
except Exception as e:
|
1870
|
+
error(f"Error during operation: {e}")
|
1871
|
+
return 1
|
1872
|
+
|
1873
|
+
|
1716
1874
|
async def handle_ec_delete(
|
1717
1875
|
client: HippiusClient, metadata_cid: str, force: bool = False
|
1718
1876
|
) -> int:
|
@@ -63,13 +63,19 @@ examples:
|
|
63
63
|
|
64
64
|
# Erasure code without publishing to global IPFS network
|
65
65
|
hippius erasure-code large_file.avi --no-publish
|
66
|
-
|
66
|
+
|
67
67
|
# Reconstruct an erasure-coded file
|
68
68
|
hippius reconstruct QmMetadataHash reconstructed_file.mp4
|
69
|
-
|
69
|
+
|
70
|
+
# Pin a CID to IPFS and publish to blockchain
|
71
|
+
hippius pin QmHash
|
72
|
+
|
73
|
+
# Pin a CID to IPFS without publishing to blockchain
|
74
|
+
hippius pin QmHash --no-publish
|
75
|
+
|
70
76
|
# Delete a file from IPFS and marketplace
|
71
77
|
hippius delete QmHash
|
72
|
-
|
78
|
+
|
73
79
|
# Delete an erasure-coded file and all its chunks
|
74
80
|
hippius ec-delete QmMetadataHash
|
75
81
|
""",
|
@@ -250,6 +256,23 @@ def add_storage_commands(subparsers):
|
|
250
256
|
help="Delete without confirmation prompt",
|
251
257
|
)
|
252
258
|
|
259
|
+
# Pin command
|
260
|
+
pin_parser = subparsers.add_parser(
|
261
|
+
"pin",
|
262
|
+
help="Pin a CID to IPFS and publish to blockchain",
|
263
|
+
)
|
264
|
+
pin_parser.add_argument("cid", help="CID to pin")
|
265
|
+
pin_parser.add_argument(
|
266
|
+
"--publish",
|
267
|
+
action="store_true",
|
268
|
+
help="Publish file to IPFS and store on the blockchain (default)",
|
269
|
+
)
|
270
|
+
pin_parser.add_argument(
|
271
|
+
"--no-publish",
|
272
|
+
action="store_true",
|
273
|
+
help="Don't publish file to blockchain (local pinning only)",
|
274
|
+
)
|
275
|
+
|
253
276
|
# Keygen command
|
254
277
|
keygen_parser = subparsers.add_parser(
|
255
278
|
"keygen", help="Generate an encryption key for secure file storage"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|