hippius 0.2.37__py3-none-any.whl → 0.2.39__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hippius
3
- Version: 0.2.37
3
+ Version: 0.2.39
4
4
  Summary: Python SDK and CLI for Hippius blockchain storage
5
5
  Home-page: https://github.com/thenervelab/hippius-sdk
6
6
  Author: Dubs
@@ -1,10 +1,10 @@
1
- hippius_sdk/__init__.py,sha256=FuqP1SPjpm3IjJbpPQtXE-1Q5HGju9bK-0TwDTy2uJY,1474
1
+ hippius_sdk/__init__.py,sha256=YRl6k_zVhkcQus3lgRi_IXvBPDgfvguZlJCtmOcp1CI,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=aGnNdXMLUurqIvKZBbbASNg4yPXw_smfof9CQWPsr-4,22958
7
+ hippius_sdk/client.py,sha256=yS7fKAYIIjLReoUg1hppCD1sRy5MbSS81aLF_JLIZRE,23275
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=Gyow5xogdjV3W8uQzAxbvROw3tL3WZU-Zz7TtQdyHWs,96337
16
+ hippius_sdk/ipfs.py,sha256=4g_NxQwmocssKu7b6kZ-ShAsF6fyKuj6IiYGCTNrpis,96918
17
17
  hippius_sdk/ipfs_core.py,sha256=X4yO9M_lSNtqiEJu5R-XX97eWzbL7GrN5MOk56p8pJk,12748
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.37.dist-info/METADATA,sha256=abEkJKwKRWH1cq_mF1Yd_HoGggvKPqE19Y9HDWn9GBs,30088
22
- hippius-0.2.37.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
23
- hippius-0.2.37.dist-info/entry_points.txt,sha256=bFAZjW3vndretf9-8s587jA2ebMVI7puhn_lVs8jPc8,149
24
- hippius-0.2.37.dist-info/RECORD,,
21
+ hippius-0.2.39.dist-info/METADATA,sha256=CbQCM8vIuc9v4rDhLma0swGipTVtQyrl7JI7mAA14dU,30088
22
+ hippius-0.2.39.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
23
+ hippius-0.2.39.dist-info/entry_points.txt,sha256=bFAZjW3vndretf9-8s587jA2ebMVI7puhn_lVs8jPc8,149
24
+ hippius-0.2.39.dist-info/RECORD,,
hippius_sdk/__init__.py CHANGED
@@ -26,7 +26,7 @@ from hippius_sdk.config import (
26
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.37"
29
+ __version__ = "0.2.39"
30
30
  __all__ = [
31
31
  "HippiusClient",
32
32
  "IPFSClient",
hippius_sdk/client.py CHANGED
@@ -3,13 +3,13 @@ Main client for the Hippius SDK.
3
3
  """
4
4
 
5
5
  import base64
6
- from typing import Any, Callable, Dict, List, Optional
6
+ from typing import Any, Callable, Dict, List, Optional, Union
7
7
 
8
8
  import nacl.secret
9
9
  import nacl.utils
10
10
 
11
11
  from hippius_sdk.config import get_config_value, get_encryption_key
12
- from hippius_sdk.ipfs import IPFSClient, S3PublishResult, S3DownloadResult
12
+ from hippius_sdk.ipfs import IPFSClient, S3PublishResult, S3PublishPin, S3DownloadResult
13
13
  from hippius_sdk.substrate import SubstrateClient
14
14
 
15
15
 
@@ -525,7 +525,8 @@ class HippiusClient:
525
525
  store_node: str = "http://localhost:5001",
526
526
  pin_node: str = "https://store.hippius.network",
527
527
  substrate_url: str = "wss://rpc.hippius.network",
528
- ) -> S3PublishResult:
528
+ publish: bool = True,
529
+ ) -> Union[S3PublishResult, S3PublishPin]:
529
530
  """
530
531
  Publish a file to IPFS and the Hippius marketplace in one operation.
531
532
 
@@ -541,10 +542,12 @@ class HippiusClient:
541
542
  bucket_name: The bucket name for key isolation
542
543
  store_node: IPFS node URL for initial upload (default: local node)
543
544
  pin_node: IPFS node URL for backup pinning (default: remote service)
544
- substrate_url: substrate url to use for the storage request.
545
+ substrate_url: substrate url to use for the storage request
546
+ publish: Whether to publish to blockchain (True) or just upload to IPFS (False)
545
547
 
546
548
  Returns:
547
- S3PublishResult: Object containing CID, file info, and transaction hash
549
+ S3PublishResult: Object containing CID, file info, and transaction hash when publish=True
550
+ S3PublishPin: Object containing CID, subaccount, file_path, pin_node, substrate_url when publish=False
548
551
 
549
552
  Raises:
550
553
  HippiusIPFSError: If IPFS operations (add or pin) fail
@@ -561,6 +564,7 @@ class HippiusClient:
561
564
  store_node,
562
565
  pin_node,
563
566
  substrate_url,
567
+ publish,
564
568
  )
565
569
 
566
570
  async def s3_download(
hippius_sdk/ipfs.py CHANGED
@@ -12,7 +12,7 @@ import shutil
12
12
  import tempfile
13
13
  import time
14
14
  import uuid
15
- from typing import Any, Callable, Dict, List, Optional
15
+ from typing import Any, Callable, Dict, List, Optional, Union
16
16
 
17
17
  import httpx
18
18
  from pydantic import BaseModel
@@ -62,6 +62,16 @@ class S3PublishResult(BaseModel):
62
62
  tx_hash: str
63
63
 
64
64
 
65
+ class S3PublishPin(BaseModel):
66
+ """Result model for s3_publish method when publish=False."""
67
+
68
+ cid: str
69
+ subaccount: str
70
+ file_path: str
71
+ pin_node: str
72
+ substrate_url: str
73
+
74
+
65
75
  class S3DownloadResult(BaseModel):
66
76
  """Result model for s3_download method."""
67
77
 
@@ -1958,7 +1968,8 @@ class IPFSClient:
1958
1968
  store_node: str = "http://localhost:5001",
1959
1969
  pin_node: str = "https://store.hippius.network",
1960
1970
  substrate_url: str = "wss://rpc.hippius.network",
1961
- ) -> S3PublishResult:
1971
+ publish: bool = True,
1972
+ ) -> Union[S3PublishResult, S3PublishPin]:
1962
1973
  """
1963
1974
  Publish a file to IPFS and the Hippius marketplace in one operation.
1964
1975
 
@@ -1980,10 +1991,12 @@ class IPFSClient:
1980
1991
  bucket_name: The bucket name for key isolation
1981
1992
  store_node: IPFS node URL for initial upload (default: local node)
1982
1993
  pin_node: IPFS node URL for backup pinning (default: remote service)
1983
- substrate_url: the substrate url to connect to for the storage request.
1994
+ substrate_url: the substrate url to connect to for the storage request
1995
+ publish: Whether to publish to blockchain (True) or just upload to IPFS (False)
1984
1996
 
1985
1997
  Returns:
1986
- S3PublishResult: Object containing CID, file info, and transaction hash
1998
+ S3PublishResult: Object containing CID, file info, and transaction hash when publish=True
1999
+ S3PublishPin: Object containing CID, subaccount, file_path, pin_node, substrate_url when publish=False
1987
2000
 
1988
2001
  Raises:
1989
2002
  HippiusIPFSError: If IPFS operations (add or pin) fail
@@ -2094,54 +2107,64 @@ class IPFSClient:
2094
2107
  f"Failed to pin file to store node {store_node}: {str(e)}"
2095
2108
  )
2096
2109
 
2097
- # Publish to substrate marketplace
2098
- try:
2099
- # Pass the seed phrase directly to avoid password prompts for encrypted config
2100
- substrate_client = SubstrateClient(
2101
- seed_phrase=seed_phrase, url=substrate_url
2102
- )
2103
- logger.info(
2104
- f"Submitting storage request to substrate for file: {filename}, CID: {cid}"
2105
- )
2110
+ # Conditionally publish to substrate marketplace based on publish flag
2111
+ if publish:
2112
+ try:
2113
+ # Pass the seed phrase directly to avoid password prompts for encrypted config
2114
+ substrate_client = SubstrateClient(
2115
+ seed_phrase=seed_phrase, url=substrate_url
2116
+ )
2117
+ logger.info(
2118
+ f"Submitting storage request to substrate for file: {filename}, CID: {cid}"
2119
+ )
2106
2120
 
2107
- tx_hash = await substrate_client.storage_request(
2108
- files=[
2109
- FileInput(
2110
- file_hash=cid,
2111
- file_name=filename,
2112
- )
2113
- ],
2114
- miner_ids=[],
2115
- seed_phrase=seed_phrase,
2116
- )
2121
+ tx_hash = await substrate_client.storage_request(
2122
+ files=[
2123
+ FileInput(
2124
+ file_hash=cid,
2125
+ file_name=filename,
2126
+ )
2127
+ ],
2128
+ miner_ids=[],
2129
+ seed_phrase=seed_phrase,
2130
+ )
2117
2131
 
2118
- logger.debug(f"Substrate call result: {tx_hash}")
2132
+ logger.debug(f"Substrate call result: {tx_hash}")
2119
2133
 
2120
- # Check if we got a valid transaction hash
2121
- if not tx_hash or tx_hash == "0x" or len(tx_hash) < 10:
2122
- logger.error(f"Invalid transaction hash received: {tx_hash}")
2123
- raise HippiusSubstrateError(
2124
- f"Invalid transaction hash received: {tx_hash}. This might indicate insufficient credits or transaction failure."
2134
+ # Check if we got a valid transaction hash
2135
+ if not tx_hash or tx_hash == "0x" or len(tx_hash) < 10:
2136
+ logger.error(f"Invalid transaction hash received: {tx_hash}")
2137
+ raise HippiusSubstrateError(
2138
+ f"Invalid transaction hash received: {tx_hash}. This might indicate insufficient credits or transaction failure."
2139
+ )
2140
+
2141
+ logger.info(
2142
+ f"Successfully published to substrate with transaction: {tx_hash}"
2125
2143
  )
2126
2144
 
2127
- logger.info(
2128
- f"Successfully published to substrate with transaction: {tx_hash}"
2145
+ except Exception as e:
2146
+ logger.error(f"Substrate call failed: {str(e)}")
2147
+ logger.debug(
2148
+ "Possible causes: insufficient credits, network issues, invalid seed phrase, or substrate node unavailability"
2149
+ )
2150
+ raise HippiusSubstrateError(f"Failed to publish to substrate: {str(e)}")
2151
+
2152
+ return S3PublishResult(
2153
+ cid=cid,
2154
+ file_name=filename,
2155
+ size_bytes=size_bytes,
2156
+ encryption_key=encryption_key_used,
2157
+ tx_hash=tx_hash,
2129
2158
  )
2130
-
2131
- except Exception as e:
2132
- logger.error(f"Substrate call failed: {str(e)}")
2133
- logger.debug(
2134
- "Possible causes: insufficient credits, network issues, invalid seed phrase, or substrate node unavailability"
2159
+ else:
2160
+ # Return S3PublishPin with required information when not publishing
2161
+ return S3PublishPin(
2162
+ cid=cid,
2163
+ subaccount=subaccount_id,
2164
+ file_path=file_path,
2165
+ pin_node=pin_node,
2166
+ substrate_url=substrate_url,
2135
2167
  )
2136
- raise HippiusSubstrateError(f"Failed to publish to substrate: {str(e)}")
2137
-
2138
- return S3PublishResult(
2139
- cid=cid,
2140
- file_name=filename,
2141
- size_bytes=size_bytes,
2142
- encryption_key=encryption_key_used,
2143
- tx_hash=tx_hash,
2144
- )
2145
2168
 
2146
2169
  async def s3_download(
2147
2170
  self,
@@ -2180,7 +2203,7 @@ class IPFSClient:
2180
2203
  """
2181
2204
  start_time = time.time()
2182
2205
 
2183
- # Download the file directly from the specified download_node
2206
+ # Download the file directly into memory from the specified download_node
2184
2207
  try:
2185
2208
  # Create parent directories if they don't exist
2186
2209
  os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True)
@@ -2188,41 +2211,29 @@ class IPFSClient:
2188
2211
  download_client = AsyncIPFSClient(api_url=download_node)
2189
2212
 
2190
2213
  download_url = f"{download_node.rstrip('/')}/api/v0/cat?arg={cid}"
2214
+
2215
+ # Download file into memory
2216
+ file_data = bytearray()
2191
2217
  async with download_client.client.stream("POST", download_url) as response:
2192
2218
  response.raise_for_status()
2219
+ async for chunk in response.aiter_bytes(chunk_size=8192):
2220
+ file_data.extend(chunk)
2193
2221
 
2194
- with open(output_path, "wb") as f:
2195
- async for chunk in response.aiter_bytes(chunk_size=8192):
2196
- f.write(chunk)
2197
-
2198
- logger.info(f"File downloaded from {download_node} with CID: {cid}")
2222
+ # Convert to bytes for consistency
2223
+ file_data = bytes(file_data)
2224
+ logger.info(f"File downloaded from {download_node} with CID: {cid} ({len(file_data)} bytes)")
2199
2225
 
2200
2226
  except Exception as e:
2201
2227
  raise HippiusIPFSError(
2202
2228
  f"Failed to download file from {download_node}: {str(e)}"
2203
2229
  )
2204
2230
 
2205
- # Get file info after download
2206
- size_bytes = os.path.getsize(output_path)
2207
- elapsed_time = time.time() - start_time
2208
-
2209
2231
  # Attempt automatic decryption if requested
2210
2232
  decrypted = False
2211
2233
  encryption_key_used = None
2234
+ final_data = file_data # This will hold either encrypted or decrypted data
2212
2235
 
2213
2236
  if auto_decrypt:
2214
- # Check if key storage is enabled and available
2215
- try:
2216
- key_storage_available = is_key_storage_enabled()
2217
- logger.debug(f"Key storage enabled: {key_storage_available}")
2218
- except ImportError:
2219
- logger.debug("Key storage module not available")
2220
- key_storage_available = False
2221
-
2222
- # Read the downloaded file content
2223
- with open(output_path, "rb") as f:
2224
- file_data = f.read()
2225
-
2226
2237
  # Check if file is empty - this indicates a problem
2227
2238
  if len(file_data) == 0:
2228
2239
  logger.error(f"Downloaded file is empty (0 bytes) for CID: {cid}")
@@ -2240,6 +2251,14 @@ class IPFSClient:
2240
2251
  decrypted = False
2241
2252
  encryption_key_used = None
2242
2253
  else:
2254
+ # Check if key storage is enabled and available
2255
+ try:
2256
+ key_storage_available = is_key_storage_enabled()
2257
+ logger.debug(f"Key storage enabled: {key_storage_available}")
2258
+ except ImportError:
2259
+ logger.debug("Key storage module not available")
2260
+ key_storage_available = False
2261
+
2243
2262
  # File has content, attempt decryption if requested
2244
2263
  decryption_attempted = False
2245
2264
  decryption_successful = False
@@ -2269,17 +2288,10 @@ class IPFSClient:
2269
2288
  existing_key_b64
2270
2289
  )
2271
2290
  box = nacl.secret.SecretBox(encryption_key_bytes)
2272
- decrypted_data = box.decrypt(file_data)
2273
-
2274
- # Write the decrypted data back to the file
2275
- with open(output_path, "wb") as f:
2276
- f.write(decrypted_data)
2291
+ final_data = box.decrypt(file_data)
2277
2292
 
2278
2293
  decryption_successful = True
2279
2294
  decrypted = True
2280
- size_bytes = len(
2281
- decrypted_data
2282
- ) # Update size to decrypted size
2283
2295
  logger.info(
2284
2296
  "Successfully decrypted file using stored key"
2285
2297
  )
@@ -2303,17 +2315,10 @@ class IPFSClient:
2303
2315
  decryption_attempted = True
2304
2316
 
2305
2317
  try:
2306
- decrypted_data = self.decrypt_data(file_data)
2307
-
2308
- # Write the decrypted data back to the file
2309
- with open(output_path, "wb") as f:
2310
- f.write(decrypted_data)
2318
+ final_data = self.decrypt_data(file_data)
2311
2319
 
2312
2320
  decryption_successful = True
2313
2321
  decrypted = True
2314
- size_bytes = len(
2315
- decrypted_data
2316
- ) # Update size to decrypted size
2317
2322
 
2318
2323
  # Store the encryption key for the result
2319
2324
  encryption_key_used = (
@@ -2338,6 +2343,14 @@ class IPFSClient:
2338
2343
  elif not decryption_attempted:
2339
2344
  logger.debug("No decryption attempted - no keys available")
2340
2345
 
2346
+ # Write the final data (encrypted or decrypted) to disk once
2347
+ with open(output_path, "wb") as f:
2348
+ f.write(final_data)
2349
+
2350
+ # Get final file info
2351
+ size_bytes = len(final_data)
2352
+ elapsed_time = time.time() - start_time
2353
+
2341
2354
  return S3DownloadResult(
2342
2355
  cid=cid,
2343
2356
  output_path=output_path,