hippius 0.2.10__py3-none-any.whl → 0.2.12__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.10
3
+ Version: 0.2.12
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
@@ -0,0 +1,17 @@
1
+ hippius_sdk/__init__.py,sha256=ZcHAg1Tk-gXHUc-AnplM-bD01IvypNFw-gIQzaanRUg,1392
2
+ hippius_sdk/cli.py,sha256=pzwoa-X5cwdA_pM-fqUyXZKHgcZODrLe4qHZuCqQMtQ,18210
3
+ hippius_sdk/cli_assets.py,sha256=V3MX63QTiex6mCp0VDXQJ7cagm5v1s4xtsu8c1O4G_k,371
4
+ hippius_sdk/cli_handlers.py,sha256=DoIyy1x8DnsXD6vjBsS_20UF_nNUGKnLHMk6HoDwdYU,129087
5
+ hippius_sdk/cli_parser.py,sha256=Qh2wgkFBUTPldvGoTQuoNKQl5Vo0x6fPEsPBU5oymP4,20242
6
+ hippius_sdk/cli_rich.py,sha256=_jTBYMdHi2--fIVwoeNi-EtkdOb6Zy_O2TUiGvU3O7s,7324
7
+ hippius_sdk/client.py,sha256=ASMFYd5qH_j-rnC-3Y0FEdlB2xPPvfs9p3SlMUXxGek,19086
8
+ hippius_sdk/config.py,sha256=wVzhVIBtijatVG7MZ3HvAwdsz_-arkTvBf5NUiQHNTo,21841
9
+ hippius_sdk/errors.py,sha256=LScJJmawVAx7aRzqqQguYSkf9iazSjEQEBNlD_GXZ6Y,1589
10
+ hippius_sdk/ipfs.py,sha256=r9GWQNp-6IOaVAZvXEtAupa_rru2u_sb8TtXFN5tkc4,74173
11
+ hippius_sdk/ipfs_core.py,sha256=Wv_nOI2k3YPUVTpOWS98X-955iuMKqH0Fc-t8ZuNLSE,12842
12
+ hippius_sdk/substrate.py,sha256=lp-GF2qfZgD_XwZwc2UNNkp-AkGUpOiZQ5aJUiea8VA,49608
13
+ hippius_sdk/utils.py,sha256=rJ611yvwKSyiBpYU3w-SuyQxoghMGU-ePuslrPv5H5g,7388
14
+ hippius-0.2.12.dist-info/METADATA,sha256=ZEozvditfm-2TXZT0Wt1qserLr6AdcblagGgVZXjA1o,29993
15
+ hippius-0.2.12.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
16
+ hippius-0.2.12.dist-info/entry_points.txt,sha256=b1lo60zRXmv1ud-c5BC-cJcAfGE5FD4qM_nia6XeQtM,98
17
+ hippius-0.2.12.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
27
27
  from hippius_sdk.utils import format_cid, format_size, hex_to_ipfs_cid
28
28
 
29
- __version__ = "0.2.10"
29
+ __version__ = "0.2.12"
30
30
  __all__ = [
31
31
  "HippiusClient",
32
32
  "IPFSClient",
@@ -102,20 +102,38 @@ def create_client(args: Any) -> HippiusClient:
102
102
  # Get substrate URL
103
103
  substrate_url = args.substrate_url if hasattr(args, "substrate_url") else None
104
104
 
105
- # Skip password if we're doing erasure-code with --no-publish
106
- # This avoids prompting for password when we don't need to interact with the blockchain
105
+ # Determine if we need to use password based on the command
106
+ # Only use password for: store, download, delete, erasure-code (unless --no-publish), reconstruct
107
107
  password = None
108
- if (
109
- hasattr(args, "command")
110
- and args.command == "erasure-code"
111
- and hasattr(args, "no_publish")
112
- and args.no_publish
113
- ):
114
- # Don't need a password in this case
115
- password = None
116
- else:
117
- # Use password from args if provided
118
- password = args.password if hasattr(args, "password") else None
108
+
109
+ # First check if password is provided as an argument
110
+ if hasattr(args, "password") and args.password:
111
+ password = args.password
112
+ # Otherwise, decide based on the command
113
+ elif hasattr(args, "command"):
114
+ command = args.command
115
+ needs_password = False
116
+
117
+ # Check if this is one of the commands that needs a password
118
+ if command in [
119
+ "store",
120
+ "store-dir",
121
+ "download",
122
+ "delete",
123
+ "delete-dir",
124
+ "reconstruct",
125
+ ]:
126
+ needs_password = True
127
+ # Special case for erasure-code - only needs password if we're publishing
128
+ elif command == "erasure-code" and not (
129
+ hasattr(args, "no_publish") and args.no_publish
130
+ ):
131
+ needs_password = True
132
+
133
+ # If this command doesn't need password access, set to empty string to skip prompting
134
+ if not needs_password:
135
+ # Use empty string to indicate "skip password prompt" to the config system
136
+ password = ""
119
137
 
120
138
  # Initialize client with provided parameters
121
139
  client = HippiusClient(
@@ -692,27 +710,32 @@ async def handle_credits(
692
710
  ):
693
711
  account_address = client.substrate_client._keypair.ss58_address
694
712
  else:
695
- # Try to get the default address
696
- default_address = get_default_address()
697
- if default_address:
698
- account_address = default_address
699
- else:
700
- has_default = get_default_address() is not None
701
-
702
- error("No account address provided, and client has no keypair.")
703
-
704
- if has_default:
705
- warning(
706
- "Please provide an account address with '--account_address' or the default address may be invalid."
707
- )
713
+ # Get the active account name and its address
714
+ from hippius_sdk.config import get_account_address, get_active_account
715
+
716
+ active_account = get_active_account()
717
+ if active_account:
718
+ active_address = get_account_address(active_account)
719
+ if active_address:
720
+ account_address = active_address
708
721
  else:
709
- warning(
710
- "Please provide an account address with '--account_address' or set a default with:"
722
+ error(
723
+ f"Active account '{active_account}' does not have a valid address."
711
724
  )
712
- log(
713
- " [bold green underline]hippius address set-default <your_account_address>[/bold green underline]"
725
+ warning(
726
+ "Please provide an account address with '--account_address'"
714
727
  )
715
-
728
+ return 1
729
+ else:
730
+ error(
731
+ "No account address provided, no active account set, and client has no keypair."
732
+ )
733
+ warning(
734
+ "Please provide an account address with '--account_address' or set an active account with:"
735
+ )
736
+ log(
737
+ " [bold green underline]hippius account switch <account_name>[/bold green underline]"
738
+ )
716
739
  return 1
717
740
 
718
741
  credits = await client.substrate_client.get_free_credits(account_address)
@@ -3160,28 +3183,32 @@ async def handle_account_balance(
3160
3183
  ):
3161
3184
  account_address = client.substrate_client._keypair.ss58_address
3162
3185
  else:
3163
- # Try to get the default address
3164
- default_address = get_default_address()
3165
- if default_address:
3166
- account_address = default_address
3167
- else:
3168
- has_default = get_default_address() is not None
3186
+ # Get the active account name and its address
3187
+ from hippius_sdk.config import get_account_address, get_active_account
3169
3188
 
3170
- error("No account address provided, and client has no keypair.")
3171
-
3172
- if has_default:
3173
- warning(
3174
- "Please provide an account address with '--account_address' or the default address may be invalid."
3175
- )
3189
+ active_account = get_active_account()
3190
+ if active_account:
3191
+ active_address = get_account_address(active_account)
3192
+ if active_address:
3193
+ account_address = active_address
3176
3194
  else:
3177
- warning(
3178
- "Please provide an account address with '--account_address' or set a default with:"
3195
+ error(
3196
+ f"Active account '{active_account}' does not have a valid address."
3179
3197
  )
3180
- log(
3181
- " hippius address set-default <your_account_address>",
3182
- style="bold blue",
3198
+ warning(
3199
+ "Please provide an account address with '--account_address'"
3183
3200
  )
3184
-
3201
+ return 1
3202
+ else:
3203
+ error(
3204
+ "No account address provided, no active account set, and client has no keypair."
3205
+ )
3206
+ warning(
3207
+ "Please provide an account address with '--account_address' or set an active account with:"
3208
+ )
3209
+ log(
3210
+ " [bold green underline]hippius account switch <account_name>[/bold green underline]"
3211
+ )
3185
3212
  return 1
3186
3213
 
3187
3214
  # Get the account balance
hippius_sdk/client.py CHANGED
@@ -85,13 +85,15 @@ class HippiusClient:
85
85
  # Initialize Substrate client
86
86
  self.substrate_client = SubstrateClient(
87
87
  url=substrate_url,
88
- seed_phrase=substrate_seed_phrase,
89
88
  password=seed_phrase_password,
90
89
  account_name=account_name,
91
90
  )
92
91
 
93
92
  async def upload_file(
94
- self, file_path: str, encrypt: Optional[bool] = None
93
+ self,
94
+ file_path: str,
95
+ encrypt: Optional[bool] = None,
96
+ seed_phrase: Optional[str] = None,
95
97
  ) -> Dict[str, Any]:
96
98
  """
97
99
  Upload a file to IPFS with optional encryption.
@@ -99,6 +101,7 @@ class HippiusClient:
99
101
  Args:
100
102
  file_path: Path to the file to upload
101
103
  encrypt: Whether to encrypt the file (overrides default)
104
+ seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
102
105
 
103
106
  Returns:
104
107
  Dict[str, Any]: Dictionary containing file details including:
@@ -114,10 +117,15 @@ class HippiusClient:
114
117
  ValueError: If encryption is requested but not available
115
118
  """
116
119
  # Use the enhanced IPFSClient method directly with encryption parameter
117
- return await self.ipfs_client.upload_file(file_path, encrypt=encrypt)
120
+ return await self.ipfs_client.upload_file(
121
+ file_path, encrypt=encrypt, seed_phrase=seed_phrase
122
+ )
118
123
 
119
124
  async def upload_directory(
120
- self, dir_path: str, encrypt: Optional[bool] = None
125
+ self,
126
+ dir_path: str,
127
+ encrypt: Optional[bool] = None,
128
+ seed_phrase: Optional[str] = None,
121
129
  ) -> Dict[str, Any]:
122
130
  """
123
131
  Upload a directory to IPFS with optional encryption.
@@ -125,6 +133,7 @@ class HippiusClient:
125
133
  Args:
126
134
  dir_path: Path to the directory to upload
127
135
  encrypt: Whether to encrypt files (overrides default)
136
+ seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
128
137
 
129
138
  Returns:
130
139
  Dict[str, Any]: Dictionary containing directory details including:
@@ -141,10 +150,16 @@ class HippiusClient:
141
150
  ValueError: If encryption is requested but not available
142
151
  """
143
152
  # Use the enhanced IPFSClient method directly with encryption parameter
144
- return await self.ipfs_client.upload_directory(dir_path, encrypt=encrypt)
153
+ return await self.ipfs_client.upload_directory(
154
+ dir_path, encrypt=encrypt, seed_phrase=seed_phrase
155
+ )
145
156
 
146
157
  async def download_file(
147
- self, cid: str, output_path: str, decrypt: Optional[bool] = None
158
+ self,
159
+ cid: str,
160
+ output_path: str,
161
+ decrypt: Optional[bool] = None,
162
+ seed_phrase: Optional[str] = None,
148
163
  ) -> Dict[str, Any]:
149
164
  """
150
165
  Download a file from IPFS with optional decryption.
@@ -154,6 +169,7 @@ class HippiusClient:
154
169
  cid: Content Identifier (CID) of the file to download
155
170
  output_path: Path where the downloaded file/directory will be saved
156
171
  decrypt: Whether to decrypt the file (overrides default)
172
+ seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
157
173
 
158
174
  Returns:
159
175
  Dict[str, Any]: Dictionary containing download details including:
@@ -169,7 +185,9 @@ class HippiusClient:
169
185
  requests.RequestException: If the download fails
170
186
  ValueError: If decryption is requested but fails
171
187
  """
172
- return await self.ipfs_client.download_file(cid, output_path, _=decrypt)
188
+ return await self.ipfs_client.download_file(
189
+ cid, output_path, _=decrypt, seed_phrase=seed_phrase
190
+ )
173
191
 
174
192
  async def cat(
175
193
  self,
@@ -177,6 +195,7 @@ class HippiusClient:
177
195
  max_display_bytes: int = 1024,
178
196
  format_output: bool = True,
179
197
  decrypt: Optional[bool] = None,
198
+ seed_phrase: Optional[str] = None,
180
199
  ) -> Dict[str, Any]:
181
200
  """
182
201
  Get the content of a file from IPFS with optional decryption.
@@ -186,6 +205,7 @@ class HippiusClient:
186
205
  max_display_bytes: Maximum number of bytes to include in the preview
187
206
  format_output: Whether to attempt to decode the content as text
188
207
  decrypt: Whether to decrypt the file (overrides default)
208
+ seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
189
209
 
190
210
  Returns:
191
211
  Dict[str, Any]: Dictionary containing content details including:
@@ -197,15 +217,22 @@ class HippiusClient:
197
217
  - decrypted: Whether the file was decrypted
198
218
  """
199
219
  return await self.ipfs_client.cat(
200
- cid, max_display_bytes, format_output, decrypt=decrypt
220
+ cid,
221
+ max_display_bytes,
222
+ format_output,
223
+ decrypt=decrypt,
224
+ seed_phrase=seed_phrase,
201
225
  )
202
226
 
203
- async def exists(self, cid: str) -> Dict[str, Any]:
227
+ async def exists(
228
+ self, cid: str, seed_phrase: Optional[str] = None
229
+ ) -> Dict[str, Any]:
204
230
  """
205
231
  Check if a CID exists on IPFS.
206
232
 
207
233
  Args:
208
234
  cid: Content Identifier (CID) to check
235
+ seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
209
236
 
210
237
  Returns:
211
238
  Dict[str, Any]: Dictionary containing:
@@ -214,14 +241,15 @@ class HippiusClient:
214
241
  - formatted_cid: Formatted version of the CID
215
242
  - gateway_url: URL to access the content if it exists
216
243
  """
217
- return await self.ipfs_client.exists(cid)
244
+ return await self.ipfs_client.exists(cid, seed_phrase=seed_phrase)
218
245
 
219
- async def pin(self, cid: str) -> Dict[str, Any]:
246
+ async def pin(self, cid: str, seed_phrase: Optional[str] = None) -> Dict[str, Any]:
220
247
  """
221
248
  Pin a CID to IPFS to keep it available.
222
249
 
223
250
  Args:
224
251
  cid: Content Identifier (CID) to pin
252
+ seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
225
253
 
226
254
  Returns:
227
255
  Dict[str, Any]: Dictionary containing:
@@ -230,7 +258,7 @@ class HippiusClient:
230
258
  - formatted_cid: Formatted version of the CID
231
259
  - message: Status message
232
260
  """
233
- return await self.ipfs_client.pin(cid)
261
+ return await self.ipfs_client.pin(cid, seed_phrase=seed_phrase)
234
262
 
235
263
  def format_cid(self, cid: str) -> str:
236
264
  """
@@ -293,6 +321,7 @@ class HippiusClient:
293
321
  encrypt: Optional[bool] = None,
294
322
  max_retries: int = 3,
295
323
  verbose: bool = True,
324
+ seed_phrase: Optional[str] = None,
296
325
  ) -> Dict[str, Any]:
297
326
  """
298
327
  Split a file using erasure coding, then upload the chunks to IPFS.
@@ -310,6 +339,7 @@ class HippiusClient:
310
339
  encrypt: Whether to encrypt the file before encoding (defaults to self.encrypt_by_default)
311
340
  max_retries: Maximum number of retry attempts for IPFS uploads
312
341
  verbose: Whether to print progress information
342
+ seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
313
343
 
314
344
  Returns:
315
345
  dict: Metadata including the original file info and chunk information
@@ -326,6 +356,7 @@ class HippiusClient:
326
356
  encrypt=encrypt,
327
357
  max_retries=max_retries,
328
358
  verbose=verbose,
359
+ seed_phrase=seed_phrase,
329
360
  )
330
361
 
331
362
  async def reconstruct_from_erasure_code(
@@ -335,6 +366,7 @@ class HippiusClient:
335
366
  temp_dir: str = None,
336
367
  max_retries: int = 3,
337
368
  verbose: bool = True,
369
+ seed_phrase: Optional[str] = None,
338
370
  ) -> Dict:
339
371
  """
340
372
  Reconstruct a file from erasure-coded chunks using its metadata.
@@ -345,6 +377,7 @@ class HippiusClient:
345
377
  temp_dir: Directory to use for temporary files (default: system temp)
346
378
  max_retries: Maximum number of retry attempts for IPFS downloads
347
379
  verbose: Whether to print progress information
380
+ seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
348
381
 
349
382
  Returns:
350
383
  Dict: containing file reconstruction info.
@@ -359,6 +392,7 @@ class HippiusClient:
359
392
  temp_dir=temp_dir,
360
393
  max_retries=max_retries,
361
394
  verbose=verbose,
395
+ seed_phrase=seed_phrase,
362
396
  )
363
397
 
364
398
  async def store_erasure_coded_file(
@@ -373,6 +407,7 @@ class HippiusClient:
373
407
  verbose: bool = True,
374
408
  progress_callback: Optional[Callable[[str, int, int], None]] = None,
375
409
  publish: bool = True,
410
+ seed_phrase: Optional[str] = None,
376
411
  ) -> Dict[str, Any]:
377
412
  """
378
413
  Erasure code a file, upload the chunks to IPFS, and store in the Hippius marketplace.
@@ -393,6 +428,7 @@ class HippiusClient:
393
428
  publish: Whether to publish to the blockchain (True) or just perform local
394
429
  erasure coding without publishing (False). When False, no password
395
430
  is needed for seed phrase access.
431
+ seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
396
432
 
397
433
  Returns:
398
434
  dict: Result including metadata CID and transaction hash (if published)
@@ -413,10 +449,14 @@ class HippiusClient:
413
449
  verbose=verbose,
414
450
  progress_callback=progress_callback,
415
451
  publish=publish,
452
+ seed_phrase=seed_phrase,
416
453
  )
417
454
 
418
455
  async def delete_file(
419
- self, cid: str, cancel_from_blockchain: bool = True
456
+ self,
457
+ cid: str,
458
+ cancel_from_blockchain: bool = True,
459
+ seed_phrase: Optional[str] = None,
420
460
  ) -> Dict[str, Any]:
421
461
  """
422
462
  Delete a file from IPFS and optionally cancel its storage on the blockchain.
@@ -424,6 +464,7 @@ class HippiusClient:
424
464
  Args:
425
465
  cid: Content Identifier (CID) of the file to delete
426
466
  cancel_from_blockchain: Whether to also cancel the storage request from the blockchain
467
+ seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
427
468
 
428
469
  Returns:
429
470
  Dict containing the result of the operation
@@ -431,13 +472,16 @@ class HippiusClient:
431
472
  Raises:
432
473
  RuntimeError: If deletion fails completely
433
474
  """
434
- return await self.ipfs_client.delete_file(cid, cancel_from_blockchain)
475
+ return await self.ipfs_client.delete_file(
476
+ cid, cancel_from_blockchain, seed_phrase=seed_phrase
477
+ )
435
478
 
436
479
  async def delete_ec_file(
437
480
  self,
438
481
  metadata_cid: str,
439
482
  cancel_from_blockchain: bool = True,
440
483
  parallel_limit: int = 20,
484
+ seed_phrase: Optional[str] = None,
441
485
  ) -> bool:
442
486
  """
443
487
  Delete an erasure-coded file, including all its chunks in parallel.
@@ -446,6 +490,7 @@ class HippiusClient:
446
490
  metadata_cid: CID of the metadata file for the erasure-coded file
447
491
  cancel_from_blockchain: Whether to cancel storage from blockchain
448
492
  parallel_limit: Maximum number of concurrent deletion operations
493
+ seed_phrase: Optional seed phrase to use for blockchain interactions (uses config if None)
449
494
 
450
495
  Returns:
451
496
  True or false if failed.
@@ -454,5 +499,8 @@ class HippiusClient:
454
499
  RuntimeError: If deletion fails completely
455
500
  """
456
501
  return await self.ipfs_client.delete_ec_file(
457
- metadata_cid, cancel_from_blockchain, parallel_limit
502
+ metadata_cid,
503
+ cancel_from_blockchain,
504
+ parallel_limit,
505
+ seed_phrase=seed_phrase,
458
506
  )
hippius_sdk/config.py CHANGED
@@ -380,7 +380,7 @@ def decrypt_seed_phrase(
380
380
  Decrypt the substrate seed phrase using password-based decryption.
381
381
 
382
382
  Args:
383
- password: Optional password (if None, will prompt)
383
+ password: Optional password (if None, will prompt; if empty string, will skip password for read-only operations)
384
384
  account_name: Optional account name (if None, uses active account)
385
385
 
386
386
  Returns:
@@ -412,6 +412,12 @@ def decrypt_seed_phrase(
412
412
  print("Error: No encrypted seed phrase found or missing salt")
413
413
  return None
414
414
 
415
+ # Check if we're in skip-password mode (empty string)
416
+ # This is used for read-only operations that don't need blockchain interaction
417
+ if password == "":
418
+ # Don't print a message as it's confusing to the user
419
+ return None
420
+
415
421
  # Get password from user if not provided
416
422
  if password is None:
417
423
  password = getpass.getpass("Enter password to decrypt seed phrase: \n\n")
@@ -427,7 +433,8 @@ def get_seed_phrase(
427
433
  Get the substrate seed phrase from configuration, decrypting if necessary.
428
434
 
429
435
  Args:
430
- password: Optional password for decryption (if None and needed, will prompt)
436
+ password: Optional password for decryption (if None and needed, will prompt;
437
+ if empty string, will skip decryption for read-only operations)
431
438
  account_name: Optional account name (if None, uses active account)
432
439
 
433
440
  Returns:
@@ -449,6 +456,11 @@ def get_seed_phrase(
449
456
  account_data = config["substrate"]["accounts"][name_to_use]
450
457
  is_encoded = account_data.get("seed_phrase_encoded", False)
451
458
 
459
+ # If password is an empty string, this indicates we're doing a read-only operation
460
+ # that doesn't require the seed phrase, so we can return None
461
+ if password == "" and is_encoded:
462
+ return None
463
+
452
464
  if is_encoded:
453
465
  return decrypt_seed_phrase(password, name_to_use)
454
466
  else: