hippius 0.1.0__py3-none-any.whl → 0.1.7__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_sdk/client.py CHANGED
@@ -3,9 +3,13 @@ Main client for the Hippius SDK.
3
3
  """
4
4
 
5
5
  import os
6
- from typing import Dict, Any, Optional, List, Union
6
+ from typing import Dict, Any, Optional, List, Union, Tuple, Set
7
7
  from hippius_sdk.ipfs import IPFSClient
8
8
  from hippius_sdk.substrate import SubstrateClient, FileInput
9
+ from hippius_sdk.config import (
10
+ get_config_value,
11
+ get_encryption_key,
12
+ )
9
13
 
10
14
 
11
15
  class HippiusClient:
@@ -17,10 +21,12 @@ class HippiusClient:
17
21
 
18
22
  def __init__(
19
23
  self,
20
- ipfs_gateway: str = "https://ipfs.io",
21
- ipfs_api_url: str = "https://relay-fr.hippius.network",
22
- substrate_url: str = None,
23
- substrate_seed_phrase: str = None,
24
+ ipfs_gateway: Optional[str] = None,
25
+ ipfs_api_url: Optional[str] = None,
26
+ substrate_url: Optional[str] = None,
27
+ substrate_seed_phrase: Optional[str] = None,
28
+ seed_phrase_password: Optional[str] = None,
29
+ account_name: Optional[str] = None,
24
30
  encrypt_by_default: Optional[bool] = None,
25
31
  encryption_key: Optional[bytes] = None,
26
32
  ):
@@ -28,14 +34,46 @@ class HippiusClient:
28
34
  Initialize the Hippius client.
29
35
 
30
36
  Args:
31
- ipfs_gateway: IPFS gateway URL for downloading content
32
- ipfs_api_url: IPFS API URL for uploading content. Defaults to Hippius relay node.
33
- substrate_url: WebSocket URL of the Hippius substrate node
34
- substrate_seed_phrase: Seed phrase for Substrate account
35
- encrypt_by_default: Whether to encrypt files by default (from .env if None)
36
- encryption_key: Encryption key for NaCl secretbox (from .env if None)
37
- """
38
- self.ipfs = IPFSClient(
37
+ ipfs_gateway: IPFS gateway URL for downloading content (from config if None)
38
+ ipfs_api_url: IPFS API URL for uploading content (from config if None)
39
+ substrate_url: WebSocket URL of the Hippius substrate node (from config if None)
40
+ substrate_seed_phrase: Seed phrase for Substrate account (from config if None)
41
+ seed_phrase_password: Password to decrypt the seed phrase if it's encrypted
42
+ account_name: Name of the account to use (uses active account if None)
43
+ encrypt_by_default: Whether to encrypt files by default (from config if None)
44
+ encryption_key: Encryption key for NaCl secretbox (from config if None)
45
+ """
46
+ # Load configuration values if not explicitly provided
47
+ if ipfs_gateway is None:
48
+ ipfs_gateway = get_config_value("ipfs", "gateway", "https://ipfs.io")
49
+
50
+ if ipfs_api_url is None:
51
+ ipfs_api_url = get_config_value(
52
+ "ipfs", "api_url", "https://relay-fr.hippius.network"
53
+ )
54
+
55
+ # Check if local IPFS is enabled in config
56
+ if get_config_value("ipfs", "local_ipfs", False):
57
+ ipfs_api_url = "http://localhost:5001"
58
+
59
+ if substrate_url is None:
60
+ substrate_url = get_config_value(
61
+ "substrate", "url", "wss://rpc.hippius.network"
62
+ )
63
+
64
+ if substrate_seed_phrase is None:
65
+ substrate_seed_phrase = get_config_value("substrate", "seed_phrase")
66
+
67
+ if encrypt_by_default is None:
68
+ encrypt_by_default = get_config_value(
69
+ "encryption", "encrypt_by_default", False
70
+ )
71
+
72
+ if encryption_key is None:
73
+ encryption_key = get_encryption_key()
74
+
75
+ # Initialize IPFS client
76
+ self.ipfs_client = IPFSClient(
39
77
  gateway=ipfs_gateway,
40
78
  api_url=ipfs_api_url,
41
79
  encrypt_by_default=encrypt_by_default,
@@ -45,7 +83,10 @@ class HippiusClient:
45
83
  # Initialize Substrate client
46
84
  try:
47
85
  self.substrate_client = SubstrateClient(
48
- url=substrate_url, seed_phrase=substrate_seed_phrase
86
+ url=substrate_url,
87
+ seed_phrase=substrate_seed_phrase,
88
+ password=seed_phrase_password,
89
+ account_name=account_name,
49
90
  )
50
91
  except Exception as e:
51
92
  print(f"Warning: Could not initialize Substrate client: {e}")
@@ -75,7 +116,7 @@ class HippiusClient:
75
116
  ValueError: If encryption is requested but not available
76
117
  """
77
118
  # Use the enhanced IPFSClient method directly with encryption parameter
78
- return self.ipfs.upload_file(file_path, encrypt=encrypt)
119
+ return self.ipfs_client.upload_file(file_path, encrypt=encrypt)
79
120
 
80
121
  def upload_directory(
81
122
  self, dir_path: str, encrypt: Optional[bool] = None
@@ -102,7 +143,7 @@ class HippiusClient:
102
143
  ValueError: If encryption is requested but not available
103
144
  """
104
145
  # Use the enhanced IPFSClient method directly with encryption parameter
105
- return self.ipfs.upload_directory(dir_path, encrypt=encrypt)
146
+ return self.ipfs_client.upload_directory(dir_path, encrypt=encrypt)
106
147
 
107
148
  def download_file(
108
149
  self, cid: str, output_path: str, decrypt: Optional[bool] = None
@@ -128,7 +169,7 @@ class HippiusClient:
128
169
  requests.RequestException: If the download fails
129
170
  ValueError: If decryption is requested but fails
130
171
  """
131
- return self.ipfs.download_file(cid, output_path, decrypt=decrypt)
172
+ return self.ipfs_client.download_file(cid, output_path, decrypt=decrypt)
132
173
 
133
174
  def cat(
134
175
  self,
@@ -155,7 +196,9 @@ class HippiusClient:
155
196
  - text_preview/hex_preview: Preview of the content
156
197
  - decrypted: Whether the file was decrypted
157
198
  """
158
- return self.ipfs.cat(cid, max_display_bytes, format_output, decrypt=decrypt)
199
+ return self.ipfs_client.cat(
200
+ cid, max_display_bytes, format_output, decrypt=decrypt
201
+ )
159
202
 
160
203
  def exists(self, cid: str) -> Dict[str, Any]:
161
204
  """
@@ -171,7 +214,7 @@ class HippiusClient:
171
214
  - formatted_cid: Formatted version of the CID
172
215
  - gateway_url: URL to access the content if it exists
173
216
  """
174
- return self.ipfs.exists(cid)
217
+ return self.ipfs_client.exists(cid)
175
218
 
176
219
  def pin(self, cid: str) -> Dict[str, Any]:
177
220
  """
@@ -187,7 +230,7 @@ class HippiusClient:
187
230
  - formatted_cid: Formatted version of the CID
188
231
  - message: Status message
189
232
  """
190
- return self.ipfs.pin(cid)
233
+ return self.ipfs_client.pin(cid)
191
234
 
192
235
  def format_cid(self, cid: str) -> str:
193
236
  """
@@ -201,7 +244,7 @@ class HippiusClient:
201
244
  Returns:
202
245
  str: Formatted CID string
203
246
  """
204
- return self.ipfs.format_cid(cid)
247
+ return self.ipfs_client.format_cid(cid)
205
248
 
206
249
  def format_size(self, size_bytes: int) -> str:
207
250
  """
@@ -215,7 +258,7 @@ class HippiusClient:
215
258
  Returns:
216
259
  str: Human-readable size string (e.g., '1.23 MB', '456.78 KB')
217
260
  """
218
- return self.ipfs.format_size(size_bytes)
261
+ return self.ipfs_client.format_size(size_bytes)
219
262
 
220
263
  def generate_encryption_key(self) -> str:
221
264
  """
@@ -244,3 +287,125 @@ class HippiusClient:
244
287
  raise ImportError(
245
288
  "PyNaCl is required for encryption. Install it with: pip install pynacl"
246
289
  )
290
+
291
+ def erasure_code_file(
292
+ self,
293
+ file_path: str,
294
+ k: int = 3,
295
+ m: int = 5,
296
+ chunk_size: int = 1024 * 1024, # 1MB chunks
297
+ encrypt: Optional[bool] = None,
298
+ max_retries: int = 3,
299
+ verbose: bool = True,
300
+ ) -> Dict[str, Any]:
301
+ """
302
+ Split a file using erasure coding, then upload the chunks to IPFS.
303
+
304
+ This implements an (m, k) Reed-Solomon code where:
305
+ - m = total number of chunks
306
+ - k = minimum chunks needed to reconstruct the file (k <= m)
307
+ - The file can be reconstructed from any k of the m chunks
308
+
309
+ Args:
310
+ file_path: Path to the file to upload
311
+ k: Number of data chunks (minimum required to reconstruct)
312
+ m: Total number of chunks (k + redundancy)
313
+ chunk_size: Size of each chunk in bytes before encoding
314
+ encrypt: Whether to encrypt the file before encoding (defaults to self.encrypt_by_default)
315
+ max_retries: Maximum number of retry attempts for IPFS uploads
316
+ verbose: Whether to print progress information
317
+
318
+ Returns:
319
+ dict: Metadata including the original file info and chunk information
320
+
321
+ Raises:
322
+ ValueError: If erasure coding is not available or parameters are invalid
323
+ RuntimeError: If chunk uploads fail
324
+ """
325
+ return self.ipfs_client.erasure_code_file(
326
+ file_path=file_path,
327
+ k=k,
328
+ m=m,
329
+ chunk_size=chunk_size,
330
+ encrypt=encrypt,
331
+ max_retries=max_retries,
332
+ verbose=verbose,
333
+ )
334
+
335
+ def reconstruct_from_erasure_code(
336
+ self,
337
+ metadata_cid: str,
338
+ output_file: str,
339
+ temp_dir: str = None,
340
+ max_retries: int = 3,
341
+ verbose: bool = True,
342
+ ) -> str:
343
+ """
344
+ Reconstruct a file from erasure-coded chunks using its metadata.
345
+
346
+ Args:
347
+ metadata_cid: IPFS CID of the metadata file
348
+ output_file: Path where the reconstructed file should be saved
349
+ temp_dir: Directory to use for temporary files (default: system temp)
350
+ max_retries: Maximum number of retry attempts for IPFS downloads
351
+ verbose: Whether to print progress information
352
+
353
+ Returns:
354
+ str: Path to the reconstructed file
355
+
356
+ Raises:
357
+ ValueError: If reconstruction fails
358
+ RuntimeError: If not enough chunks can be downloaded
359
+ """
360
+ return self.ipfs_client.reconstruct_from_erasure_code(
361
+ metadata_cid=metadata_cid,
362
+ output_file=output_file,
363
+ temp_dir=temp_dir,
364
+ max_retries=max_retries,
365
+ verbose=verbose,
366
+ )
367
+
368
+ def store_erasure_coded_file(
369
+ self,
370
+ file_path: str,
371
+ k: int = 3,
372
+ m: int = 5,
373
+ chunk_size: int = 1024 * 1024, # 1MB chunks
374
+ encrypt: Optional[bool] = None,
375
+ miner_ids: List[str] = None,
376
+ max_retries: int = 3,
377
+ verbose: bool = True,
378
+ ) -> Dict[str, Any]:
379
+ """
380
+ Erasure code a file, upload the chunks to IPFS, and store in the Hippius marketplace.
381
+
382
+ This is a convenience method that combines erasure_code_file with storage_request.
383
+
384
+ Args:
385
+ file_path: Path to the file to upload
386
+ k: Number of data chunks (minimum required to reconstruct)
387
+ m: Total number of chunks (k + redundancy)
388
+ chunk_size: Size of each chunk in bytes before encoding
389
+ encrypt: Whether to encrypt the file before encoding
390
+ miner_ids: List of specific miner IDs to use for storage
391
+ max_retries: Maximum number of retry attempts
392
+ verbose: Whether to print progress information
393
+
394
+ Returns:
395
+ dict: Result including metadata CID and transaction hash
396
+
397
+ Raises:
398
+ ValueError: If parameters are invalid
399
+ RuntimeError: If processing fails
400
+ """
401
+ return self.ipfs_client.store_erasure_coded_file(
402
+ file_path=file_path,
403
+ k=k,
404
+ m=m,
405
+ chunk_size=chunk_size,
406
+ encrypt=encrypt,
407
+ miner_ids=miner_ids,
408
+ substrate_client=self.substrate_client,
409
+ max_retries=max_retries,
410
+ verbose=verbose,
411
+ )