hippius 0.2.18__py3-none-any.whl → 0.2.19__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.18.dist-info → hippius-0.2.19.dist-info}/METADATA +1 -1
- {hippius-0.2.18.dist-info → hippius-0.2.19.dist-info}/RECORD +7 -7
- hippius_sdk/__init__.py +4 -2
- hippius_sdk/ipfs.py +167 -11
- hippius_sdk/key_storage.py +19 -2
- {hippius-0.2.18.dist-info → hippius-0.2.19.dist-info}/WHEEL +0 -0
- {hippius-0.2.18.dist-info → hippius-0.2.19.dist-info}/entry_points.txt +0 -0
@@ -1,4 +1,4 @@
|
|
1
|
-
hippius_sdk/__init__.py,sha256=
|
1
|
+
hippius_sdk/__init__.py,sha256=j0kPfN608bcRYKQzkq-8192AzFoHWkIxvMULfGYyNxs,1474
|
2
2
|
hippius_sdk/cli.py,sha256=aqKOYSBSWt7UhcpFt7wf9yIPJ3bznpsJ6ehOnuZ4usI,18235
|
3
3
|
hippius_sdk/cli_assets.py,sha256=V3MX63QTiex6mCp0VDXQJ7cagm5v1s4xtsu8c1O4G_k,371
|
4
4
|
hippius_sdk/cli_handlers.py,sha256=TQNE9os87gRzRKLEO-SIwhFnBtEFMiaSESv-Bu7omfo,128909
|
@@ -12,12 +12,12 @@ hippius_sdk/db/migrations/20241201000001_create_key_storage_tables.sql,sha256=mi
|
|
12
12
|
hippius_sdk/db/setup_database.sh,sha256=bDeIiTnMuX0AYCdefAfEI1CyXho6A6kLzufsDZFSXpE,3118
|
13
13
|
hippius_sdk/db_utils.py,sha256=-x0rbN0as7Tn3PJPZBYCgreZe52FLH40ppA1TLxsg90,1851
|
14
14
|
hippius_sdk/errors.py,sha256=LScJJmawVAx7aRzqqQguYSkf9iazSjEQEBNlD_GXZ6Y,1589
|
15
|
-
hippius_sdk/ipfs.py,sha256=
|
15
|
+
hippius_sdk/ipfs.py,sha256=Bp-f4QjrPeLKy61SxNsmB_YWvVjcYVBJlTt4NN6mGWo,90907
|
16
16
|
hippius_sdk/ipfs_core.py,sha256=eOOgLoyP9mvwndnCjldnTc7z94ImYCXY3nm7JU3e_Mo,12676
|
17
|
-
hippius_sdk/key_storage.py,sha256
|
17
|
+
hippius_sdk/key_storage.py,sha256=-f4pEUvkczY_yw3lYJ_kM2iDDOAtKOgrexWxVgIo78w,9486
|
18
18
|
hippius_sdk/substrate.py,sha256=AqfQNl5Qv_s6roOESSdwleX0-VGyu5sh3m5-dYZp5ek,49079
|
19
19
|
hippius_sdk/utils.py,sha256=rJ611yvwKSyiBpYU3w-SuyQxoghMGU-ePuslrPv5H5g,7388
|
20
|
-
hippius-0.2.
|
21
|
-
hippius-0.2.
|
22
|
-
hippius-0.2.
|
23
|
-
hippius-0.2.
|
20
|
+
hippius-0.2.19.dist-info/METADATA,sha256=fzIdrxmqDpYRA_Y8QBCUA4PtlfpYHby30WeHUFkq758,30088
|
21
|
+
hippius-0.2.19.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
|
22
|
+
hippius-0.2.19.dist-info/entry_points.txt,sha256=bFAZjW3vndretf9-8s587jA2ebMVI7puhn_lVs8jPc8,149
|
23
|
+
hippius-0.2.19.dist-info/RECORD,,
|
hippius_sdk/__init__.py
CHANGED
@@ -23,13 +23,15 @@ from hippius_sdk.config import (
|
|
23
23
|
set_encryption_key,
|
24
24
|
set_seed_phrase,
|
25
25
|
)
|
26
|
-
from hippius_sdk.ipfs import IPFSClient
|
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.19"
|
30
30
|
__all__ = [
|
31
31
|
"HippiusClient",
|
32
32
|
"IPFSClient",
|
33
|
+
"S3PublishResult",
|
34
|
+
"S3DownloadResult",
|
33
35
|
"get_config_value",
|
34
36
|
"set_config_value",
|
35
37
|
"get_encryption_key",
|
hippius_sdk/ipfs.py
CHANGED
@@ -13,7 +13,11 @@ import tempfile
|
|
13
13
|
import time
|
14
14
|
import uuid
|
15
15
|
from typing import Any, Callable, Dict, List, Optional
|
16
|
-
|
16
|
+
from hippius_sdk.key_storage import (
|
17
|
+
generate_and_store_key_for_seed,
|
18
|
+
get_key_for_seed,
|
19
|
+
is_key_storage_enabled,
|
20
|
+
)
|
17
21
|
import httpx
|
18
22
|
from pydantic import BaseModel
|
19
23
|
|
@@ -57,6 +61,18 @@ class S3PublishResult(BaseModel):
|
|
57
61
|
tx_hash: str
|
58
62
|
|
59
63
|
|
64
|
+
class S3DownloadResult(BaseModel):
|
65
|
+
"""Result model for s3_download method."""
|
66
|
+
|
67
|
+
cid: str
|
68
|
+
output_path: str
|
69
|
+
size_bytes: int
|
70
|
+
size_formatted: str
|
71
|
+
elapsed_seconds: float
|
72
|
+
decrypted: bool
|
73
|
+
encryption_key: Optional[str]
|
74
|
+
|
75
|
+
|
60
76
|
# Set up logger for this module
|
61
77
|
logger = logging.getLogger(__name__)
|
62
78
|
|
@@ -1949,14 +1965,7 @@ class IPFSClient:
|
|
1949
1965
|
encryption_key_used = None
|
1950
1966
|
if encrypt:
|
1951
1967
|
# Check if key storage is enabled and available
|
1952
|
-
key_storage_available = False
|
1953
1968
|
try:
|
1954
|
-
from hippius_sdk.key_storage import (
|
1955
|
-
generate_and_store_key_for_seed,
|
1956
|
-
get_key_for_seed,
|
1957
|
-
is_key_storage_enabled,
|
1958
|
-
)
|
1959
|
-
|
1960
1969
|
key_storage_available = is_key_storage_enabled()
|
1961
1970
|
logger.debug(f"Key storage enabled: {key_storage_available}")
|
1962
1971
|
except ImportError:
|
@@ -1964,9 +1973,6 @@ class IPFSClient:
|
|
1964
1973
|
key_storage_available = False
|
1965
1974
|
|
1966
1975
|
if key_storage_available:
|
1967
|
-
# Use PostgreSQL-backed key storage
|
1968
|
-
logger.info(f"Using PostgreSQL key storage for seed phrase")
|
1969
|
-
|
1970
1976
|
# Try to get existing key for this seed phrase
|
1971
1977
|
existing_key_b64 = await get_key_for_seed(seed_phrase)
|
1972
1978
|
|
@@ -2079,3 +2085,153 @@ class IPFSClient:
|
|
2079
2085
|
encryption_key=encryption_key_used,
|
2080
2086
|
tx_hash=tx_hash,
|
2081
2087
|
)
|
2088
|
+
|
2089
|
+
async def s3_download(
|
2090
|
+
self, cid: str, output_path: str, seed_phrase: str, auto_decrypt: bool = True
|
2091
|
+
) -> S3DownloadResult:
|
2092
|
+
"""
|
2093
|
+
Download a file from IPFS with automatic decryption.
|
2094
|
+
|
2095
|
+
This method automatically manages decryption keys per seed phrase:
|
2096
|
+
- Downloads the file from IPFS
|
2097
|
+
- If auto_decrypt=True, attempts to decrypt using stored keys for the seed phrase
|
2098
|
+
- Falls back to client encryption key if key storage is not available
|
2099
|
+
- Returns the file in decrypted form if decryption succeeds
|
2100
|
+
|
2101
|
+
Args:
|
2102
|
+
cid: Content Identifier (CID) of the file to download
|
2103
|
+
output_path: Path where the downloaded file will be saved
|
2104
|
+
seed_phrase: Seed phrase to use for retrieving decryption keys
|
2105
|
+
auto_decrypt: Whether to attempt automatic decryption (default: True)
|
2106
|
+
|
2107
|
+
Returns:
|
2108
|
+
S3DownloadResult: Object containing download info and decryption status
|
2109
|
+
|
2110
|
+
Raises:
|
2111
|
+
HippiusIPFSError: If IPFS download fails
|
2112
|
+
FileNotFoundError: If the output directory doesn't exist
|
2113
|
+
ValueError: If decryption fails
|
2114
|
+
"""
|
2115
|
+
start_time = time.time()
|
2116
|
+
|
2117
|
+
# Download the file first using the existing download_file method
|
2118
|
+
try:
|
2119
|
+
# Create parent directories if they don't exist
|
2120
|
+
os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True)
|
2121
|
+
|
2122
|
+
# Download without decryption first
|
2123
|
+
download_result = await self.download_file(
|
2124
|
+
cid=cid,
|
2125
|
+
output_path=output_path,
|
2126
|
+
skip_directory_check=False,
|
2127
|
+
seed_phrase=seed_phrase,
|
2128
|
+
)
|
2129
|
+
|
2130
|
+
if not download_result.get("success", False):
|
2131
|
+
raise HippiusIPFSError("Download failed")
|
2132
|
+
|
2133
|
+
except Exception as e:
|
2134
|
+
raise HippiusIPFSError(f"Failed to download file from IPFS: {str(e)}")
|
2135
|
+
|
2136
|
+
# Get file info after download
|
2137
|
+
size_bytes = os.path.getsize(output_path)
|
2138
|
+
elapsed_time = time.time() - start_time
|
2139
|
+
|
2140
|
+
# Attempt automatic decryption if requested
|
2141
|
+
decrypted = False
|
2142
|
+
encryption_key_used = None
|
2143
|
+
|
2144
|
+
if auto_decrypt:
|
2145
|
+
# Check if key storage is enabled and available
|
2146
|
+
try:
|
2147
|
+
key_storage_available = is_key_storage_enabled()
|
2148
|
+
logger.debug(f"Key storage enabled: {key_storage_available}")
|
2149
|
+
except ImportError:
|
2150
|
+
logger.debug("Key storage module not available")
|
2151
|
+
key_storage_available = False
|
2152
|
+
|
2153
|
+
# Read the downloaded file content
|
2154
|
+
with open(output_path, "rb") as f:
|
2155
|
+
file_data = f.read()
|
2156
|
+
|
2157
|
+
decryption_attempted = False
|
2158
|
+
decryption_successful = False
|
2159
|
+
|
2160
|
+
if key_storage_available:
|
2161
|
+
# Try to get the encryption key for this seed phrase
|
2162
|
+
try:
|
2163
|
+
existing_key_b64 = await get_key_for_seed(seed_phrase)
|
2164
|
+
|
2165
|
+
if existing_key_b64:
|
2166
|
+
logger.debug("Found encryption key for seed phrase, attempting decryption")
|
2167
|
+
decryption_attempted = True
|
2168
|
+
encryption_key_used = existing_key_b64
|
2169
|
+
|
2170
|
+
# Attempt decryption with the stored key
|
2171
|
+
try:
|
2172
|
+
import nacl.secret
|
2173
|
+
|
2174
|
+
encryption_key_bytes = base64.b64decode(existing_key_b64)
|
2175
|
+
box = nacl.secret.SecretBox(encryption_key_bytes)
|
2176
|
+
decrypted_data = box.decrypt(file_data)
|
2177
|
+
|
2178
|
+
# Write the decrypted data back to the file
|
2179
|
+
with open(output_path, "wb") as f:
|
2180
|
+
f.write(decrypted_data)
|
2181
|
+
|
2182
|
+
decryption_successful = True
|
2183
|
+
decrypted = True
|
2184
|
+
size_bytes = len(decrypted_data) # Update size to decrypted size
|
2185
|
+
logger.info("Successfully decrypted file using stored key")
|
2186
|
+
|
2187
|
+
except Exception as decrypt_error:
|
2188
|
+
logger.debug(f"Decryption failed with stored key: {decrypt_error}")
|
2189
|
+
# Continue to try fallback decryption
|
2190
|
+
else:
|
2191
|
+
logger.debug("No encryption key found for seed phrase")
|
2192
|
+
|
2193
|
+
except Exception as e:
|
2194
|
+
logger.debug(f"Error retrieving key from storage: {e}")
|
2195
|
+
|
2196
|
+
# If key storage decryption failed or wasn't available, try client encryption key
|
2197
|
+
if not decryption_successful and self.encryption_available:
|
2198
|
+
logger.debug("Attempting decryption with client encryption key")
|
2199
|
+
decryption_attempted = True
|
2200
|
+
|
2201
|
+
try:
|
2202
|
+
decrypted_data = self.decrypt_data(file_data)
|
2203
|
+
|
2204
|
+
# Write the decrypted data back to the file
|
2205
|
+
with open(output_path, "wb") as f:
|
2206
|
+
f.write(decrypted_data)
|
2207
|
+
|
2208
|
+
decryption_successful = True
|
2209
|
+
decrypted = True
|
2210
|
+
size_bytes = len(decrypted_data) # Update size to decrypted size
|
2211
|
+
|
2212
|
+
# Store the encryption key for the result
|
2213
|
+
encryption_key_used = (
|
2214
|
+
base64.b64encode(self.encryption_key).decode("utf-8")
|
2215
|
+
if self.encryption_key
|
2216
|
+
else None
|
2217
|
+
)
|
2218
|
+
logger.info("Successfully decrypted file using client encryption key")
|
2219
|
+
|
2220
|
+
except Exception as decrypt_error:
|
2221
|
+
logger.debug(f"Decryption failed with client key: {decrypt_error}")
|
2222
|
+
|
2223
|
+
# Log final decryption status
|
2224
|
+
if decryption_attempted and not decryption_successful:
|
2225
|
+
logger.info("File may not be encrypted or decryption keys don't match")
|
2226
|
+
elif not decryption_attempted:
|
2227
|
+
logger.debug("No decryption attempted - no keys available")
|
2228
|
+
|
2229
|
+
return S3DownloadResult(
|
2230
|
+
cid=cid,
|
2231
|
+
output_path=output_path,
|
2232
|
+
size_bytes=size_bytes,
|
2233
|
+
size_formatted=self.format_size(size_bytes),
|
2234
|
+
elapsed_seconds=round(elapsed_time, 2),
|
2235
|
+
decrypted=decrypted,
|
2236
|
+
encryption_key=encryption_key_used,
|
2237
|
+
)
|
hippius_sdk/key_storage.py
CHANGED
@@ -232,8 +232,25 @@ _default_storage = None
|
|
232
232
|
|
233
233
|
|
234
234
|
def is_key_storage_enabled() -> bool:
|
235
|
-
"""
|
236
|
-
|
235
|
+
"""
|
236
|
+
Check if key storage is enabled and available.
|
237
|
+
|
238
|
+
Returns True if:
|
239
|
+
1. Explicitly enabled in config, OR
|
240
|
+
2. asyncpg is available (key_storage extra installed) AND not explicitly disabled
|
241
|
+
"""
|
242
|
+
# Check if explicitly disabled
|
243
|
+
config_value = get_config_value("key_storage", "enabled", None)
|
244
|
+
if config_value is False:
|
245
|
+
return False
|
246
|
+
|
247
|
+
# If explicitly enabled, return True
|
248
|
+
if config_value is True:
|
249
|
+
return True
|
250
|
+
|
251
|
+
# If not set in config, auto-detect based on asyncpg availability
|
252
|
+
# This allows users who install [key_storage] extra to use it without manual config
|
253
|
+
return ASYNCPG_AVAILABLE
|
237
254
|
|
238
255
|
|
239
256
|
def get_default_storage() -> KeyStorage:
|
File without changes
|
File without changes
|