hippius 0.2.38__py3-none-any.whl → 0.2.40__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.38.dist-info → hippius-0.2.40.dist-info}/METADATA +1 -1
- {hippius-0.2.38.dist-info → hippius-0.2.40.dist-info}/RECORD +8 -8
- hippius_sdk/__init__.py +2 -3
- hippius_sdk/client.py +6 -1
- hippius_sdk/ipfs.py +56 -52
- hippius_sdk/ipfs_core.py +9 -4
- {hippius-0.2.38.dist-info → hippius-0.2.40.dist-info}/WHEEL +0 -0
- {hippius-0.2.38.dist-info → hippius-0.2.40.dist-info}/entry_points.txt +0 -0
@@ -1,10 +1,10 @@
|
|
1
|
-
hippius_sdk/__init__.py,sha256=
|
1
|
+
hippius_sdk/__init__.py,sha256=T73Sgflb4AIuVZqLNsFeHDwFziBsqy2lQktpNLezg8I,1474
|
2
2
|
hippius_sdk/cli.py,sha256=aqKOYSBSWt7UhcpFt7wf9yIPJ3bznpsJ6ehOnuZ4usI,18235
|
3
3
|
hippius_sdk/cli_assets.py,sha256=rjH3Z5A1CQr2d5CIAAAb0WMCjoZZlMWcdo0f93KqluE,635
|
4
4
|
hippius_sdk/cli_handlers.py,sha256=HkZldE8ZDS6WHu8aSoeS_rYZ4kp3F-Kdzu-weY1c0vU,128258
|
5
5
|
hippius_sdk/cli_parser.py,sha256=z7UvgWvvy04ey-R56qZiCqYc_9RaNq1rVDkQyXoK3JU,21100
|
6
6
|
hippius_sdk/cli_rich.py,sha256=_jTBYMdHi2--fIVwoeNi-EtkdOb6Zy_O2TUiGvU3O7s,7324
|
7
|
-
hippius_sdk/client.py,sha256=
|
7
|
+
hippius_sdk/client.py,sha256=FU6OWvuZjqlizXONixDjggpEEvnp4KkaG8DYvdxRiBY,23411
|
8
8
|
hippius_sdk/config.py,sha256=Hf_aUYzG9ylzqauA_ABUSSB5mBTYbp-VtB36VQt2XDw,21981
|
9
9
|
hippius_sdk/db/README.md,sha256=okDeI1qgkaZqXSlJ8L0xIE4UpuxO-qEGPIbXUvSHQjU,2030
|
10
10
|
hippius_sdk/db/env.db.template,sha256=_6hEC3IvkzCDOAzG1_yJUKRUfCTMciNaJUicZpMCat4,217
|
@@ -13,12 +13,12 @@ hippius_sdk/db/migrations/20241202000001_switch_to_subaccount_encryption.sql,sha
|
|
13
13
|
hippius_sdk/db/setup_database.sh,sha256=STp03qxkp2RmIVr6YZIcvQQm-_LLUOb6Jobh-52HWmg,3115
|
14
14
|
hippius_sdk/db_utils.py,sha256=-x0rbN0as7Tn3PJPZBYCgreZe52FLH40ppA1TLxsg90,1851
|
15
15
|
hippius_sdk/errors.py,sha256=LScJJmawVAx7aRzqqQguYSkf9iazSjEQEBNlD_GXZ6Y,1589
|
16
|
-
hippius_sdk/ipfs.py,sha256=
|
17
|
-
hippius_sdk/ipfs_core.py,sha256=
|
16
|
+
hippius_sdk/ipfs.py,sha256=QG2BzLSK2dkiD_wVj04DPiVmaWVLn2hUBoRj32AvrBY,97252
|
17
|
+
hippius_sdk/ipfs_core.py,sha256=WG7bGLk-threOvmumizwh1dnd5zqbIkTXy1y-BRGayI,12789
|
18
18
|
hippius_sdk/key_storage.py,sha256=SXFd6aGQw9MDLGX2vSBuAY7rdX-k5EvFm63z7_n-8yQ,8148
|
19
19
|
hippius_sdk/substrate.py,sha256=4a7UIE4UqGcDW7luKTBgSDqfb2OIZusB39G1UiRs_YU,50158
|
20
20
|
hippius_sdk/utils.py,sha256=rJ611yvwKSyiBpYU3w-SuyQxoghMGU-ePuslrPv5H5g,7388
|
21
|
-
hippius-0.2.
|
22
|
-
hippius-0.2.
|
23
|
-
hippius-0.2.
|
24
|
-
hippius-0.2.
|
21
|
+
hippius-0.2.40.dist-info/METADATA,sha256=hxmHtVzaMN4l6YwrEeeP8WV20IWEVHa9bq6AkidvD0I,30088
|
22
|
+
hippius-0.2.40.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
|
23
|
+
hippius-0.2.40.dist-info/entry_points.txt,sha256=bFAZjW3vndretf9-8s587jA2ebMVI7puhn_lVs8jPc8,149
|
24
|
+
hippius-0.2.40.dist-info/RECORD,,
|
hippius_sdk/__init__.py
CHANGED
@@ -23,15 +23,14 @@ from hippius_sdk.config import (
|
|
23
23
|
set_encryption_key,
|
24
24
|
set_seed_phrase,
|
25
25
|
)
|
26
|
-
from hippius_sdk.ipfs import IPFSClient, S3PublishResult,
|
26
|
+
from hippius_sdk.ipfs import IPFSClient, S3PublishResult, S3DownloadResult
|
27
27
|
from hippius_sdk.utils import format_cid, format_size, hex_to_ipfs_cid
|
28
28
|
|
29
|
-
__version__ = "0.2.
|
29
|
+
__version__ = "0.2.40"
|
30
30
|
__all__ = [
|
31
31
|
"HippiusClient",
|
32
32
|
"IPFSClient",
|
33
33
|
"S3PublishResult",
|
34
|
-
"S3PublishPin",
|
35
34
|
"S3DownloadResult",
|
36
35
|
"get_config_value",
|
37
36
|
"set_config_value",
|
hippius_sdk/client.py
CHANGED
@@ -118,7 +118,9 @@ class HippiusClient:
|
|
118
118
|
"""
|
119
119
|
# Use the enhanced IPFSClient method directly with encryption parameter
|
120
120
|
return await self.ipfs_client.upload_file(
|
121
|
-
file_path,
|
121
|
+
file_path,
|
122
|
+
encrypt=encrypt,
|
123
|
+
seed_phrase=seed_phrase,
|
122
124
|
)
|
123
125
|
|
124
126
|
async def upload_directory(
|
@@ -522,6 +524,7 @@ class HippiusClient:
|
|
522
524
|
seed_phrase: str,
|
523
525
|
subaccount_id: str,
|
524
526
|
bucket_name: str,
|
527
|
+
file_name: str = None,
|
525
528
|
store_node: str = "http://localhost:5001",
|
526
529
|
pin_node: str = "https://store.hippius.network",
|
527
530
|
substrate_url: str = "wss://rpc.hippius.network",
|
@@ -535,6 +538,7 @@ class HippiusClient:
|
|
535
538
|
2. Pins to pin_node (remote) for persistence and backup
|
536
539
|
|
537
540
|
Args:
|
541
|
+
file_name: The original file name.
|
538
542
|
file_path: Path to the file to publish
|
539
543
|
encrypt: Whether to encrypt the file before uploading
|
540
544
|
seed_phrase: Seed phrase for blockchain transaction signing
|
@@ -565,6 +569,7 @@ class HippiusClient:
|
|
565
569
|
pin_node,
|
566
570
|
substrate_url,
|
567
571
|
publish,
|
572
|
+
file_name=file_name,
|
568
573
|
)
|
569
574
|
|
570
575
|
async def s3_download(
|
hippius_sdk/ipfs.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
"""
|
2
2
|
IPFS operations for the Hippius SDK.
|
3
3
|
"""
|
4
|
+
|
4
5
|
import asyncio
|
5
6
|
import base64
|
6
7
|
import hashlib
|
@@ -129,7 +130,7 @@ class IPFSClient:
|
|
129
130
|
|
130
131
|
try:
|
131
132
|
self.client = AsyncIPFSClient(api_url=api_url, gateway=self.gateway)
|
132
|
-
except httpx.ConnectError
|
133
|
+
except httpx.ConnectError:
|
133
134
|
print(
|
134
135
|
f"Warning: Falling back to local IPFS daemon, but still using gateway={self.gateway}"
|
135
136
|
)
|
@@ -241,6 +242,7 @@ class IPFSClient:
|
|
241
242
|
encrypt: Optional[bool] = None,
|
242
243
|
max_retries: int = 3,
|
243
244
|
seed_phrase: Optional[str] = None,
|
245
|
+
file_name: str = None,
|
244
246
|
) -> Dict[str, Any]:
|
245
247
|
"""
|
246
248
|
Upload a file to IPFS with optional encryption.
|
@@ -251,6 +253,7 @@ class IPFSClient:
|
|
251
253
|
encrypt: Whether to encrypt the file (overrides default)
|
252
254
|
max_retries: Maximum number of retry attempts (default: 3)
|
253
255
|
seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
|
256
|
+
file_name: The original file name
|
254
257
|
|
255
258
|
Returns:
|
256
259
|
Dict[str, Any]: Dictionary containing:
|
@@ -278,7 +281,7 @@ class IPFSClient:
|
|
278
281
|
)
|
279
282
|
|
280
283
|
# Get file info before upload
|
281
|
-
|
284
|
+
file_name = file_name if file_name else os.path.basename(file_path)
|
282
285
|
size_bytes = os.path.getsize(file_path)
|
283
286
|
|
284
287
|
# If encryption is requested, encrypt the file first
|
@@ -303,7 +306,10 @@ class IPFSClient:
|
|
303
306
|
# Use the original file for upload
|
304
307
|
upload_path = file_path
|
305
308
|
|
306
|
-
result = await self.client.add_file(
|
309
|
+
result = await self.client.add_file(
|
310
|
+
upload_path,
|
311
|
+
file_name=file_name,
|
312
|
+
)
|
307
313
|
cid = result["Hash"]
|
308
314
|
|
309
315
|
finally:
|
@@ -314,7 +320,7 @@ class IPFSClient:
|
|
314
320
|
# Format the result
|
315
321
|
result = {
|
316
322
|
"cid": cid,
|
317
|
-
"filename":
|
323
|
+
"filename": file_name,
|
318
324
|
"size_bytes": size_bytes,
|
319
325
|
"encrypted": should_encrypt,
|
320
326
|
}
|
@@ -1411,7 +1417,7 @@ class IPFSClient:
|
|
1411
1417
|
|
1412
1418
|
for i, chunk in enumerate(reconstructed_chunks):
|
1413
1419
|
if verbose and i % 10 == 0:
|
1414
|
-
print(f"Processing chunk {i+1}/{len(reconstructed_chunks)}...")
|
1420
|
+
print(f"Processing chunk {i + 1}/{len(reconstructed_chunks)}...")
|
1415
1421
|
|
1416
1422
|
# For all chunks except the last one, use full chunk size
|
1417
1423
|
if i < len(reconstructed_chunks) - 1:
|
@@ -1750,8 +1756,9 @@ class IPFSClient:
|
|
1750
1756
|
# Record the child files that were processed
|
1751
1757
|
result["child_files"] = child_files
|
1752
1758
|
except Exception as e:
|
1753
|
-
print(
|
1754
|
-
|
1759
|
+
print(
|
1760
|
+
f"Warning: Failed to check if CID is a directory: {e}"
|
1761
|
+
) # Continue with regular file unpin
|
1755
1762
|
|
1756
1763
|
# Now unpin the main file/directory
|
1757
1764
|
if unpin:
|
@@ -1821,7 +1828,8 @@ class IPFSClient:
|
|
1821
1828
|
cancel_from_blockchain: bool = True,
|
1822
1829
|
parallel_limit: int = 20,
|
1823
1830
|
seed_phrase: Optional[str] = None,
|
1824
|
-
metadata_timeout: int = 30,
|
1831
|
+
metadata_timeout: int = 30,
|
1832
|
+
# Timeout in seconds for metadata fetch
|
1825
1833
|
) -> bool:
|
1826
1834
|
"""
|
1827
1835
|
Delete an erasure-coded file, including all its chunks in parallel.
|
@@ -1863,8 +1871,7 @@ class IPFSClient:
|
|
1863
1871
|
except asyncio.TimeoutError:
|
1864
1872
|
print(
|
1865
1873
|
f"Timed out after {metadata_timeout}s waiting for metadata download"
|
1866
|
-
)
|
1867
|
-
# We'll continue with blockchain cancellation even without metadata
|
1874
|
+
) # We'll continue with blockchain cancellation even without metadata
|
1868
1875
|
except json.JSONDecodeError as e:
|
1869
1876
|
# If we can't parse the metadata JSON, record the error but continue
|
1870
1877
|
print(f"Error parsing metadata JSON: {e}")
|
@@ -1969,6 +1976,7 @@ class IPFSClient:
|
|
1969
1976
|
pin_node: str = "https://store.hippius.network",
|
1970
1977
|
substrate_url: str = "wss://rpc.hippius.network",
|
1971
1978
|
publish: bool = True,
|
1979
|
+
file_name: str = None,
|
1972
1980
|
) -> Union[S3PublishResult, S3PublishPin]:
|
1973
1981
|
"""
|
1974
1982
|
Publish a file to IPFS and the Hippius marketplace in one operation.
|
@@ -1993,6 +2001,7 @@ class IPFSClient:
|
|
1993
2001
|
pin_node: IPFS node URL for backup pinning (default: remote service)
|
1994
2002
|
substrate_url: the substrate url to connect to for the storage request
|
1995
2003
|
publish: Whether to publish to blockchain (True) or just upload to IPFS (False)
|
2004
|
+
file_name: The original file name.
|
1996
2005
|
|
1997
2006
|
Returns:
|
1998
2007
|
S3PublishResult: Object containing CID, file info, and transaction hash when publish=True
|
@@ -2089,7 +2098,10 @@ class IPFSClient:
|
|
2089
2098
|
# Step 1: Upload to store_node (local) for immediate availability
|
2090
2099
|
try:
|
2091
2100
|
store_client = AsyncIPFSClient(api_url=store_node)
|
2092
|
-
result = await store_client.add_file(
|
2101
|
+
result = await store_client.add_file(
|
2102
|
+
file_path,
|
2103
|
+
file_name=file_name,
|
2104
|
+
)
|
2093
2105
|
cid = result["Hash"]
|
2094
2106
|
logger.info(f"File uploaded to store node {store_node} with CID: {cid}")
|
2095
2107
|
except Exception as e:
|
@@ -2112,7 +2124,8 @@ class IPFSClient:
|
|
2112
2124
|
try:
|
2113
2125
|
# Pass the seed phrase directly to avoid password prompts for encrypted config
|
2114
2126
|
substrate_client = SubstrateClient(
|
2115
|
-
seed_phrase=seed_phrase,
|
2127
|
+
seed_phrase=seed_phrase,
|
2128
|
+
url=substrate_url,
|
2116
2129
|
)
|
2117
2130
|
logger.info(
|
2118
2131
|
f"Submitting storage request to substrate for file: {filename}, CID: {cid}"
|
@@ -2203,7 +2216,7 @@ class IPFSClient:
|
|
2203
2216
|
"""
|
2204
2217
|
start_time = time.time()
|
2205
2218
|
|
2206
|
-
# Download the file directly from the specified download_node
|
2219
|
+
# Download the file directly into memory from the specified download_node
|
2207
2220
|
try:
|
2208
2221
|
# Create parent directories if they don't exist
|
2209
2222
|
os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True)
|
@@ -2211,41 +2224,31 @@ class IPFSClient:
|
|
2211
2224
|
download_client = AsyncIPFSClient(api_url=download_node)
|
2212
2225
|
|
2213
2226
|
download_url = f"{download_node.rstrip('/')}/api/v0/cat?arg={cid}"
|
2227
|
+
|
2228
|
+
# Download file into memory
|
2229
|
+
file_data = bytearray()
|
2214
2230
|
async with download_client.client.stream("POST", download_url) as response:
|
2215
2231
|
response.raise_for_status()
|
2232
|
+
async for chunk in response.aiter_bytes(chunk_size=8192):
|
2233
|
+
file_data.extend(chunk)
|
2216
2234
|
|
2217
|
-
|
2218
|
-
|
2219
|
-
|
2220
|
-
|
2221
|
-
|
2235
|
+
# Convert to bytes for consistency
|
2236
|
+
file_data = bytes(file_data)
|
2237
|
+
logger.info(
|
2238
|
+
f"File downloaded from {download_node} with CID: {cid} ({len(file_data)} bytes)"
|
2239
|
+
)
|
2222
2240
|
|
2223
2241
|
except Exception as e:
|
2224
2242
|
raise HippiusIPFSError(
|
2225
2243
|
f"Failed to download file from {download_node}: {str(e)}"
|
2226
2244
|
)
|
2227
2245
|
|
2228
|
-
# Get file info after download
|
2229
|
-
size_bytes = os.path.getsize(output_path)
|
2230
|
-
elapsed_time = time.time() - start_time
|
2231
|
-
|
2232
2246
|
# Attempt automatic decryption if requested
|
2233
2247
|
decrypted = False
|
2234
2248
|
encryption_key_used = None
|
2249
|
+
final_data = file_data # This will hold either encrypted or decrypted data
|
2235
2250
|
|
2236
2251
|
if auto_decrypt:
|
2237
|
-
# Check if key storage is enabled and available
|
2238
|
-
try:
|
2239
|
-
key_storage_available = is_key_storage_enabled()
|
2240
|
-
logger.debug(f"Key storage enabled: {key_storage_available}")
|
2241
|
-
except ImportError:
|
2242
|
-
logger.debug("Key storage module not available")
|
2243
|
-
key_storage_available = False
|
2244
|
-
|
2245
|
-
# Read the downloaded file content
|
2246
|
-
with open(output_path, "rb") as f:
|
2247
|
-
file_data = f.read()
|
2248
|
-
|
2249
2252
|
# Check if file is empty - this indicates a problem
|
2250
2253
|
if len(file_data) == 0:
|
2251
2254
|
logger.error(f"Downloaded file is empty (0 bytes) for CID: {cid}")
|
@@ -2263,6 +2266,14 @@ class IPFSClient:
|
|
2263
2266
|
decrypted = False
|
2264
2267
|
encryption_key_used = None
|
2265
2268
|
else:
|
2269
|
+
# Check if key storage is enabled and available
|
2270
|
+
try:
|
2271
|
+
key_storage_available = is_key_storage_enabled()
|
2272
|
+
logger.debug(f"Key storage enabled: {key_storage_available}")
|
2273
|
+
except ImportError:
|
2274
|
+
logger.debug("Key storage module not available")
|
2275
|
+
key_storage_available = False
|
2276
|
+
|
2266
2277
|
# File has content, attempt decryption if requested
|
2267
2278
|
decryption_attempted = False
|
2268
2279
|
decryption_successful = False
|
@@ -2292,17 +2303,10 @@ class IPFSClient:
|
|
2292
2303
|
existing_key_b64
|
2293
2304
|
)
|
2294
2305
|
box = nacl.secret.SecretBox(encryption_key_bytes)
|
2295
|
-
|
2296
|
-
|
2297
|
-
# Write the decrypted data back to the file
|
2298
|
-
with open(output_path, "wb") as f:
|
2299
|
-
f.write(decrypted_data)
|
2306
|
+
final_data = box.decrypt(file_data)
|
2300
2307
|
|
2301
2308
|
decryption_successful = True
|
2302
2309
|
decrypted = True
|
2303
|
-
size_bytes = len(
|
2304
|
-
decrypted_data
|
2305
|
-
) # Update size to decrypted size
|
2306
2310
|
logger.info(
|
2307
2311
|
"Successfully decrypted file using stored key"
|
2308
2312
|
)
|
@@ -2310,8 +2314,7 @@ class IPFSClient:
|
|
2310
2314
|
except Exception as decrypt_error:
|
2311
2315
|
logger.debug(
|
2312
2316
|
f"Decryption failed with stored key: {decrypt_error}"
|
2313
|
-
)
|
2314
|
-
# Continue to try fallback decryption
|
2317
|
+
) # Continue to try fallback decryption
|
2315
2318
|
else:
|
2316
2319
|
logger.debug(
|
2317
2320
|
"No encryption key found for account+bucket combination"
|
@@ -2326,17 +2329,10 @@ class IPFSClient:
|
|
2326
2329
|
decryption_attempted = True
|
2327
2330
|
|
2328
2331
|
try:
|
2329
|
-
|
2330
|
-
|
2331
|
-
# Write the decrypted data back to the file
|
2332
|
-
with open(output_path, "wb") as f:
|
2333
|
-
f.write(decrypted_data)
|
2332
|
+
final_data = self.decrypt_data(file_data)
|
2334
2333
|
|
2335
2334
|
decryption_successful = True
|
2336
2335
|
decrypted = True
|
2337
|
-
size_bytes = len(
|
2338
|
-
decrypted_data
|
2339
|
-
) # Update size to decrypted size
|
2340
2336
|
|
2341
2337
|
# Store the encryption key for the result
|
2342
2338
|
encryption_key_used = (
|
@@ -2361,6 +2357,14 @@ class IPFSClient:
|
|
2361
2357
|
elif not decryption_attempted:
|
2362
2358
|
logger.debug("No decryption attempted - no keys available")
|
2363
2359
|
|
2360
|
+
# Write the final data (encrypted or decrypted) to disk once
|
2361
|
+
with open(output_path, "wb") as f:
|
2362
|
+
f.write(final_data)
|
2363
|
+
|
2364
|
+
# Get final file info
|
2365
|
+
size_bytes = len(final_data)
|
2366
|
+
elapsed_time = time.time() - start_time
|
2367
|
+
|
2364
2368
|
return S3DownloadResult(
|
2365
2369
|
cid=cid,
|
2366
2370
|
output_path=output_path,
|
hippius_sdk/ipfs_core.py
CHANGED
@@ -48,11 +48,16 @@ class AsyncIPFSClient:
|
|
48
48
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
49
49
|
await self.close()
|
50
50
|
|
51
|
-
async def add_file(
|
51
|
+
async def add_file(
|
52
|
+
self,
|
53
|
+
file_path: str,
|
54
|
+
file_name: str = None,
|
55
|
+
) -> Dict[str, Any]:
|
52
56
|
"""
|
53
57
|
Add a file to IPFS.
|
54
58
|
|
55
59
|
Args:
|
60
|
+
file_name: Name of the file
|
56
61
|
file_path: Path to the file to add
|
57
62
|
|
58
63
|
Returns:
|
@@ -60,9 +65,9 @@ class AsyncIPFSClient:
|
|
60
65
|
"""
|
61
66
|
with open(file_path, "rb") as f:
|
62
67
|
file_content = f.read()
|
63
|
-
|
64
|
-
|
65
|
-
|
68
|
+
file_name = file_name if file_name else os.path.basename(file_path)
|
69
|
+
files = {"file": (file_name, file_content, "application/octet-stream")}
|
70
|
+
|
66
71
|
# Explicitly set wrap-with-directory=false to prevent wrapping in directory
|
67
72
|
response = await self.client.post(
|
68
73
|
f"{self.api_url}/api/v0/add?wrap-with-directory=false&cid-version=1",
|
File without changes
|
File without changes
|