@cmdoss/memwal-sdk 0.6.2 โ†’ 0.8.0

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.
Files changed (247) hide show
  1. package/ARCHITECTURE.md +547 -547
  2. package/BENCHMARKS.md +238 -238
  3. package/README.md +310 -181
  4. package/dist/ai-sdk/tools.d.ts +2 -2
  5. package/dist/ai-sdk/tools.js +2 -2
  6. package/dist/client/ClientMemoryManager.js +2 -2
  7. package/dist/client/ClientMemoryManager.js.map +1 -1
  8. package/dist/client/PersonalDataWallet.d.ts.map +1 -1
  9. package/dist/client/SimplePDWClient.d.ts +29 -1
  10. package/dist/client/SimplePDWClient.d.ts.map +1 -1
  11. package/dist/client/SimplePDWClient.js +45 -13
  12. package/dist/client/SimplePDWClient.js.map +1 -1
  13. package/dist/client/namespaces/EmbeddingsNamespace.d.ts +1 -1
  14. package/dist/client/namespaces/EmbeddingsNamespace.js +1 -1
  15. package/dist/client/namespaces/MemoryNamespace.d.ts +31 -0
  16. package/dist/client/namespaces/MemoryNamespace.d.ts.map +1 -1
  17. package/dist/client/namespaces/MemoryNamespace.js +272 -39
  18. package/dist/client/namespaces/MemoryNamespace.js.map +1 -1
  19. package/dist/client/namespaces/consolidated/AINamespace.d.ts +2 -2
  20. package/dist/client/namespaces/consolidated/AINamespace.js +2 -2
  21. package/dist/client/namespaces/consolidated/BlockchainNamespace.d.ts +12 -2
  22. package/dist/client/namespaces/consolidated/BlockchainNamespace.d.ts.map +1 -1
  23. package/dist/client/namespaces/consolidated/BlockchainNamespace.js +62 -4
  24. package/dist/client/namespaces/consolidated/BlockchainNamespace.js.map +1 -1
  25. package/dist/client/namespaces/consolidated/StorageNamespace.d.ts +67 -2
  26. package/dist/client/namespaces/consolidated/StorageNamespace.d.ts.map +1 -1
  27. package/dist/client/namespaces/consolidated/StorageNamespace.js +549 -16
  28. package/dist/client/namespaces/consolidated/StorageNamespace.js.map +1 -1
  29. package/dist/config/ConfigurationHelper.js +61 -61
  30. package/dist/config/defaults.js +2 -2
  31. package/dist/config/defaults.js.map +1 -1
  32. package/dist/graph/GraphService.js +21 -21
  33. package/dist/graph/GraphService.js.map +1 -1
  34. package/dist/index.d.ts +3 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +3 -1
  37. package/dist/index.js.map +1 -1
  38. package/dist/infrastructure/seal/EncryptionService.d.ts +9 -5
  39. package/dist/infrastructure/seal/EncryptionService.d.ts.map +1 -1
  40. package/dist/infrastructure/seal/EncryptionService.js +37 -15
  41. package/dist/infrastructure/seal/EncryptionService.js.map +1 -1
  42. package/dist/infrastructure/seal/SealService.d.ts +13 -5
  43. package/dist/infrastructure/seal/SealService.d.ts.map +1 -1
  44. package/dist/infrastructure/seal/SealService.js +36 -34
  45. package/dist/infrastructure/seal/SealService.js.map +1 -1
  46. package/dist/langchain/createPDWRAG.js +30 -30
  47. package/dist/retrieval/MemoryDecryptionPipeline.d.ts.map +1 -1
  48. package/dist/retrieval/MemoryDecryptionPipeline.js +2 -1
  49. package/dist/retrieval/MemoryDecryptionPipeline.js.map +1 -1
  50. package/dist/retrieval/MemoryRetrievalService.d.ts +31 -0
  51. package/dist/retrieval/MemoryRetrievalService.d.ts.map +1 -1
  52. package/dist/retrieval/MemoryRetrievalService.js +44 -4
  53. package/dist/retrieval/MemoryRetrievalService.js.map +1 -1
  54. package/dist/services/CapabilityService.d.ts.map +1 -1
  55. package/dist/services/CapabilityService.js +30 -14
  56. package/dist/services/CapabilityService.js.map +1 -1
  57. package/dist/services/CrossContextPermissionService.d.ts.map +1 -1
  58. package/dist/services/CrossContextPermissionService.js +9 -7
  59. package/dist/services/CrossContextPermissionService.js.map +1 -1
  60. package/dist/services/EmbeddingService.d.ts +28 -1
  61. package/dist/services/EmbeddingService.d.ts.map +1 -1
  62. package/dist/services/EmbeddingService.js +54 -0
  63. package/dist/services/EmbeddingService.js.map +1 -1
  64. package/dist/services/EncryptionService.d.ts.map +1 -1
  65. package/dist/services/EncryptionService.js +6 -5
  66. package/dist/services/EncryptionService.js.map +1 -1
  67. package/dist/services/GeminiAIService.js +309 -309
  68. package/dist/services/IndexManager.d.ts +5 -1
  69. package/dist/services/IndexManager.d.ts.map +1 -1
  70. package/dist/services/IndexManager.js +17 -40
  71. package/dist/services/IndexManager.js.map +1 -1
  72. package/dist/services/QueryService.js +1 -1
  73. package/dist/services/QueryService.js.map +1 -1
  74. package/dist/services/StorageService.d.ts +11 -0
  75. package/dist/services/StorageService.d.ts.map +1 -1
  76. package/dist/services/StorageService.js +73 -10
  77. package/dist/services/StorageService.js.map +1 -1
  78. package/dist/services/TransactionService.d.ts +20 -0
  79. package/dist/services/TransactionService.d.ts.map +1 -1
  80. package/dist/services/TransactionService.js +43 -0
  81. package/dist/services/TransactionService.js.map +1 -1
  82. package/dist/services/ViewService.js +2 -2
  83. package/dist/services/ViewService.js.map +1 -1
  84. package/dist/services/storage/QuiltBatchManager.d.ts +101 -1
  85. package/dist/services/storage/QuiltBatchManager.d.ts.map +1 -1
  86. package/dist/services/storage/QuiltBatchManager.js +410 -20
  87. package/dist/services/storage/QuiltBatchManager.js.map +1 -1
  88. package/dist/services/storage/index.d.ts +1 -1
  89. package/dist/services/storage/index.d.ts.map +1 -1
  90. package/dist/services/storage/index.js.map +1 -1
  91. package/dist/utils/LRUCache.d.ts +106 -0
  92. package/dist/utils/LRUCache.d.ts.map +1 -0
  93. package/dist/utils/LRUCache.js +281 -0
  94. package/dist/utils/LRUCache.js.map +1 -0
  95. package/dist/utils/index.d.ts +1 -0
  96. package/dist/utils/index.d.ts.map +1 -1
  97. package/dist/utils/index.js +2 -0
  98. package/dist/utils/index.js.map +1 -1
  99. package/dist/utils/memoryIndexOnChain.d.ts +212 -0
  100. package/dist/utils/memoryIndexOnChain.d.ts.map +1 -0
  101. package/dist/utils/memoryIndexOnChain.js +312 -0
  102. package/dist/utils/memoryIndexOnChain.js.map +1 -0
  103. package/dist/utils/rebuildIndexNode.d.ts +29 -0
  104. package/dist/utils/rebuildIndexNode.d.ts.map +1 -1
  105. package/dist/utils/rebuildIndexNode.js +366 -98
  106. package/dist/utils/rebuildIndexNode.js.map +1 -1
  107. package/dist/vector/HnswWasmService.d.ts +20 -5
  108. package/dist/vector/HnswWasmService.d.ts.map +1 -1
  109. package/dist/vector/HnswWasmService.js +73 -40
  110. package/dist/vector/HnswWasmService.js.map +1 -1
  111. package/dist/vector/IHnswService.d.ts +10 -1
  112. package/dist/vector/IHnswService.d.ts.map +1 -1
  113. package/dist/vector/IHnswService.js.map +1 -1
  114. package/dist/vector/NodeHnswService.d.ts +16 -0
  115. package/dist/vector/NodeHnswService.d.ts.map +1 -1
  116. package/dist/vector/NodeHnswService.js +84 -5
  117. package/dist/vector/NodeHnswService.js.map +1 -1
  118. package/dist/vector/createHnswService.d.ts +1 -1
  119. package/dist/vector/createHnswService.js +1 -1
  120. package/dist/vector/index.d.ts +1 -1
  121. package/dist/vector/index.js +1 -1
  122. package/package.json +157 -157
  123. package/src/access/PermissionService.ts +635 -635
  124. package/src/aggregation/AggregationService.ts +389 -389
  125. package/src/ai-sdk/PDWVectorStore.ts +715 -715
  126. package/src/ai-sdk/index.ts +65 -65
  127. package/src/ai-sdk/tools.ts +460 -460
  128. package/src/ai-sdk/types.ts +404 -404
  129. package/src/batch/BatchManager.ts +597 -597
  130. package/src/batch/BatchingService.ts +429 -429
  131. package/src/batch/MemoryProcessingCache.ts +492 -492
  132. package/src/batch/index.ts +30 -30
  133. package/src/browser.ts +200 -200
  134. package/src/client/ClientMemoryManager.ts +987 -987
  135. package/src/client/PersonalDataWallet.ts +345 -345
  136. package/src/client/SimplePDWClient.ts +1289 -1222
  137. package/src/client/factory.ts +154 -154
  138. package/src/client/namespaces/AnalyticsNamespace.ts +377 -377
  139. package/src/client/namespaces/BatchNamespace.ts +356 -356
  140. package/src/client/namespaces/CacheNamespace.ts +123 -123
  141. package/src/client/namespaces/CapabilityNamespace.ts +217 -217
  142. package/src/client/namespaces/ClassifyNamespace.ts +169 -169
  143. package/src/client/namespaces/ContextNamespace.ts +297 -297
  144. package/src/client/namespaces/EmbeddingsNamespace.ts +99 -99
  145. package/src/client/namespaces/EncryptionNamespace.ts +221 -221
  146. package/src/client/namespaces/GraphNamespace.ts +468 -468
  147. package/src/client/namespaces/IndexNamespace.ts +361 -361
  148. package/src/client/namespaces/MemoryNamespace.ts +1422 -1135
  149. package/src/client/namespaces/PermissionsNamespace.ts +254 -254
  150. package/src/client/namespaces/PipelineNamespace.ts +220 -220
  151. package/src/client/namespaces/SearchNamespace.ts +1049 -1049
  152. package/src/client/namespaces/StorageNamespace.ts +458 -458
  153. package/src/client/namespaces/TxNamespace.ts +260 -260
  154. package/src/client/namespaces/WalletNamespace.ts +243 -243
  155. package/src/client/namespaces/consolidated/AINamespace.ts +449 -449
  156. package/src/client/namespaces/consolidated/BlockchainNamespace.ts +607 -546
  157. package/src/client/namespaces/consolidated/SecurityNamespace.ts +648 -648
  158. package/src/client/namespaces/consolidated/StorageNamespace.ts +1141 -497
  159. package/src/client/namespaces/consolidated/index.ts +39 -39
  160. package/src/client/signers/KeypairSigner.ts +108 -108
  161. package/src/client/signers/UnifiedSigner.ts +110 -110
  162. package/src/client/signers/WalletAdapterSigner.ts +159 -159
  163. package/src/client/signers/index.ts +26 -26
  164. package/src/config/ConfigurationHelper.ts +412 -412
  165. package/src/config/defaults.ts +51 -51
  166. package/src/config/index.ts +8 -8
  167. package/src/config/validation.ts +70 -70
  168. package/src/core/index.ts +14 -14
  169. package/src/core/interfaces/IService.ts +307 -307
  170. package/src/core/interfaces/index.ts +8 -8
  171. package/src/core/types/capability.ts +297 -297
  172. package/src/core/types/index.ts +870 -870
  173. package/src/core/types/wallet.ts +270 -270
  174. package/src/core/types.ts +9 -9
  175. package/src/core/wallet.ts +222 -222
  176. package/src/embedding/index.ts +19 -19
  177. package/src/embedding/types.ts +357 -357
  178. package/src/errors/index.ts +602 -602
  179. package/src/errors/recovery.ts +461 -461
  180. package/src/errors/validation.ts +567 -567
  181. package/src/generated/pdw/capability.ts +319 -319
  182. package/src/graph/GraphService.ts +887 -887
  183. package/src/graph/KnowledgeGraphManager.ts +728 -728
  184. package/src/graph/index.ts +25 -25
  185. package/src/index.ts +498 -474
  186. package/src/infrastructure/index.ts +22 -22
  187. package/src/infrastructure/seal/EncryptionService.ts +628 -603
  188. package/src/infrastructure/seal/SealService.ts +613 -615
  189. package/src/infrastructure/seal/index.ts +9 -9
  190. package/src/infrastructure/sui/BlockchainManager.ts +627 -627
  191. package/src/infrastructure/sui/SuiService.ts +888 -888
  192. package/src/infrastructure/sui/index.ts +9 -9
  193. package/src/infrastructure/walrus/StorageManager.ts +604 -604
  194. package/src/infrastructure/walrus/WalrusStorageService.ts +612 -612
  195. package/src/infrastructure/walrus/index.ts +9 -9
  196. package/src/langchain/PDWEmbeddings.ts +145 -145
  197. package/src/langchain/PDWVectorStore.ts +456 -456
  198. package/src/langchain/createPDWRAG.ts +303 -303
  199. package/src/langchain/index.ts +47 -47
  200. package/src/permissions/ConsentRepository.browser.ts +249 -249
  201. package/src/permissions/ConsentRepository.ts +364 -364
  202. package/src/pipeline/MemoryPipeline.ts +862 -862
  203. package/src/pipeline/PipelineManager.ts +683 -683
  204. package/src/pipeline/index.ts +26 -26
  205. package/src/retrieval/AdvancedSearchService.ts +629 -629
  206. package/src/retrieval/MemoryAnalyticsService.ts +711 -711
  207. package/src/retrieval/MemoryDecryptionPipeline.ts +825 -824
  208. package/src/retrieval/MemoryRetrievalService.ts +904 -830
  209. package/src/retrieval/index.ts +42 -42
  210. package/src/services/BatchService.ts +352 -352
  211. package/src/services/CapabilityService.ts +464 -448
  212. package/src/services/ClassifierService.ts +465 -465
  213. package/src/services/CrossContextPermissionService.ts +486 -484
  214. package/src/services/EmbeddingService.ts +771 -706
  215. package/src/services/EncryptionService.ts +712 -711
  216. package/src/services/GeminiAIService.ts +753 -753
  217. package/src/services/IndexManager.ts +977 -1004
  218. package/src/services/MemoryIndexService.ts +1003 -1003
  219. package/src/services/MemoryService.ts +369 -369
  220. package/src/services/QueryService.ts +890 -890
  221. package/src/services/StorageService.ts +1182 -1111
  222. package/src/services/TransactionService.ts +838 -790
  223. package/src/services/VectorService.ts +462 -462
  224. package/src/services/ViewService.ts +484 -484
  225. package/src/services/index.ts +25 -25
  226. package/src/services/storage/BlobAttributesManager.ts +333 -333
  227. package/src/services/storage/KnowledgeGraphManager.ts +425 -425
  228. package/src/services/storage/MemorySearchManager.ts +387 -387
  229. package/src/services/storage/QuiltBatchManager.ts +1130 -660
  230. package/src/services/storage/WalrusMetadataManager.ts +268 -268
  231. package/src/services/storage/WalrusStorageManager.ts +287 -287
  232. package/src/services/storage/index.ts +57 -52
  233. package/src/types/index.ts +13 -13
  234. package/src/utils/LRUCache.ts +378 -0
  235. package/src/utils/index.ts +76 -68
  236. package/src/utils/memoryIndexOnChain.ts +507 -0
  237. package/src/utils/rebuildIndex.ts +290 -290
  238. package/src/utils/rebuildIndexNode.ts +771 -424
  239. package/src/vector/BrowserHnswIndexService.ts +758 -758
  240. package/src/vector/HnswWasmService.ts +731 -679
  241. package/src/vector/IHnswService.ts +233 -224
  242. package/src/vector/NodeHnswService.ts +833 -735
  243. package/src/vector/VectorManager.ts +478 -478
  244. package/src/vector/createHnswService.ts +135 -135
  245. package/src/vector/index.ts +56 -56
  246. package/src/wallet/ContextWalletService.ts +656 -656
  247. package/src/wallet/MainWalletService.ts +317 -317
@@ -1,825 +1,826 @@
1
- /**
2
- * MemoryDecryptionPipeline - Seamless SEAL-based Memory Decryption
3
- *
4
- * Provides comprehensive decryption capabilities for encrypted memories using
5
- * Mysten's SEAL SDK with official testnet key servers and configurable infrastructure.
6
- *
7
- * Features:
8
- * - ๐Ÿ” Seamless SEAL decryption integration
9
- * - ๐Ÿ”‘ Automatic key server configuration (testnet/mainnet)
10
- * - โšก Batch decryption optimization
11
- * - ๐Ÿ›ก๏ธ Secure session key management
12
- * - ๐Ÿ”„ Automatic retry and fallback mechanisms
13
- * - ๐Ÿ“Š Decryption analytics and monitoring
14
- * - ๐ŸŒ Environment-based configuration
15
- */
16
-
17
- import { SealClient, SessionKey } from '@mysten/seal';
18
- import { Transaction } from '@mysten/sui/transactions';
19
- import { fromHex, toHex } from '@mysten/sui/utils';
20
- import { EncryptionService } from '../services/EncryptionService';
21
- import { StorageManager } from '../infrastructure/walrus/StorageManager';
22
- import { UnifiedMemoryResult } from '../retrieval/MemoryRetrievalService';
23
-
24
- // Key server configurations from SEAL documentation
25
- export interface KeyServerConfig {
26
- name: string;
27
- mode: 'open' | 'permissioned';
28
- objectId: string;
29
- url: string;
30
- provider: string;
31
- network: 'testnet' | 'mainnet';
32
- weight?: number;
33
- isDefault?: boolean;
34
- }
35
-
36
- export interface DecryptionConfig {
37
- // Key server configuration
38
- keyServers?: KeyServerConfig[];
39
- defaultKeyServerMode?: 'open' | 'permissioned';
40
- customKeyServerUrl?: string;
41
- customKeyServerObjectId?: string;
42
-
43
- // Session management
44
- sessionKeyTTL?: number; // minutes
45
- maxSessionKeys?: number;
46
- autoRefreshSession?: boolean;
47
-
48
- // Performance options
49
- enableBatchDecryption?: boolean;
50
- batchSize?: number;
51
- maxConcurrentDecryptions?: number;
52
- decryptionTimeout?: number; // ms
53
-
54
- // Fallback options
55
- enableFallback?: boolean;
56
- maxRetryAttempts?: number;
57
- retryDelayMs?: number;
58
-
59
- // Security options
60
- verifyKeyServers?: boolean;
61
- enableDecryptionAudit?: boolean;
62
- requireOwnershipVerification?: boolean;
63
- }
64
-
65
- export interface DecryptionRequest {
66
- memoryId: string;
67
- encryptedContent: string;
68
- contentHash?: string;
69
- userAddress: string;
70
- ownerAddress?: string;
71
- sessionKey?: SessionKey;
72
- metadata?: Record<string, any>;
73
- }
74
-
75
- export interface DecryptionResult {
76
- memoryId: string;
77
- decryptedContent: string;
78
- contentHash: string;
79
- isVerified: boolean;
80
- decryptionTime: number;
81
- keyServerUsed: string;
82
- sessionKeyId: string;
83
- }
84
-
85
- export interface BatchDecryptionResult {
86
- successful: DecryptionResult[];
87
- failed: Array<{
88
- memoryId: string;
89
- error: string;
90
- retryCount: number;
91
- }>;
92
- stats: {
93
- totalRequests: number;
94
- successCount: number;
95
- failureCount: number;
96
- totalProcessingTime: number;
97
- averageDecryptionTime: number;
98
- keyServerPerformance: Record<string, {
99
- requests: number;
100
- successes: number;
101
- averageTime: number;
102
- }>;
103
- };
104
- }
105
-
106
- /**
107
- * Memory Decryption Pipeline Service
108
- */
109
- export class MemoryDecryptionPipeline {
110
- private sealClient: SealClient | null = null;
111
- private encryptionService: EncryptionService;
112
- private storageManager: StorageManager;
113
- private config: DecryptionConfig;
114
-
115
- // Session key management
116
- private sessionKeys = new Map<string, SessionKey>();
117
- private sessionKeyTimestamps = new Map<string, number>();
118
-
119
- // Decryption cache
120
- private decryptionCache = new Map<string, { content: string; timestamp: number }>();
121
- private readonly CACHE_TTL = 30 * 60 * 1000; // 30 minutes
122
-
123
- // Performance monitoring
124
- private decryptionStats = {
125
- totalDecryptions: 0,
126
- successfulDecryptions: 0,
127
- failedDecryptions: 0,
128
- totalDecryptionTime: 0,
129
- keyServerStats: new Map<string, { requests: number; successes: number; totalTime: number }>()
130
- };
131
-
132
- // Official Mysten Labs testnet key servers from documentation
133
- private static readonly DEFAULT_TESTNET_SERVERS: KeyServerConfig[] = [
134
- {
135
- name: 'mysten-testnet-1',
136
- mode: 'open',
137
- objectId: '0x73d05d62c18d9374e3ea529e8e0ed6161da1a141a94d3f76ae3fe4e99356db75',
138
- url: 'https://seal-key-server-testnet-1.mystenlabs.com',
139
- provider: 'Mysten Labs',
140
- network: 'testnet',
141
- weight: 1,
142
- isDefault: true
143
- },
144
- {
145
- name: 'mysten-testnet-2',
146
- mode: 'open',
147
- objectId: '0xf5d14a81a982144ae441cd7d64b09027f116a468bd36e7eca494f750591623c8',
148
- url: 'https://seal-key-server-testnet-2.mystenlabs.com',
149
- provider: 'Mysten Labs',
150
- network: 'testnet',
151
- weight: 1,
152
- isDefault: true
153
- }
154
- ];
155
-
156
- // Additional verified testnet key servers
157
- private static readonly VERIFIED_TESTNET_SERVERS: KeyServerConfig[] = [
158
- {
159
- name: 'ruby-nodes-testnet',
160
- mode: 'open',
161
- objectId: '0x6068c0acb197dddbacd4746a9de7f025b2ed5a5b6c1b1ab44dade4426d141da2',
162
- url: 'https://seal-testnet.api.rubynodes.io',
163
- provider: 'Ruby Nodes',
164
- network: 'testnet',
165
- weight: 1
166
- },
167
- {
168
- name: 'nodeinfra-testnet',
169
- mode: 'open',
170
- objectId: '0x5466b7df5c15b508678d51496ada8afab0d6f70a01c10613123382b1b8131007',
171
- url: 'https://open-seal-testnet.nodeinfra.com',
172
- provider: 'NodeInfra',
173
- network: 'testnet',
174
- weight: 1
175
- },
176
- {
177
- name: 'studio-mirai-testnet',
178
- mode: 'open',
179
- objectId: '0x164ac3d2b3b8694b8181c13f671950004765c23f270321a45fdd04d40cccf0f2',
180
- url: 'https://open.key-server-testnet.seal.mirai.cloud',
181
- provider: 'Studio Mirai',
182
- network: 'testnet',
183
- weight: 1
184
- },
185
- {
186
- name: 'overclock-testnet',
187
- mode: 'open',
188
- objectId: '0x9c949e53c36ab7a9c484ed9e8b43267a77d4b8d70e79aa6b39042e3d4c434105',
189
- url: 'https://seal-testnet-open.overclock.run',
190
- provider: 'Overclock',
191
- network: 'testnet',
192
- weight: 1
193
- },
194
- {
195
- name: 'h2o-nodes-testnet',
196
- mode: 'open',
197
- objectId: '0x39cef09b24b667bc6ed54f7159d82352fe2d5dd97ca9a5beaa1d21aa774f25a2',
198
- url: 'https://seal-open.sui-testnet.h2o-nodes.com',
199
- provider: 'H2O Nodes',
200
- network: 'testnet',
201
- weight: 1
202
- },
203
- {
204
- name: 'triton-one-testnet',
205
- mode: 'open',
206
- objectId: '0x4cded1abeb52a22b6becb42a91d3686a4c901cf52eee16234214d0b5b2da4c46',
207
- url: 'https://seal.testnet.sui.rpcpool.com',
208
- provider: 'Triton One',
209
- network: 'testnet',
210
- weight: 1
211
- }
212
- ];
213
-
214
- constructor(
215
- encryptionService: EncryptionService,
216
- storageManager: StorageManager,
217
- config?: Partial<DecryptionConfig>
218
- ) {
219
- this.encryptionService = encryptionService;
220
- this.storageManager = storageManager;
221
- this.config = this.mergeWithDefaults(config || {});
222
- this.initializeDecryptionPipeline();
223
- }
224
-
225
- // ==================== INITIALIZATION ====================
226
-
227
- /**
228
- * Merge user config with environment variables and defaults
229
- */
230
- private mergeWithDefaults(userConfig: Partial<DecryptionConfig>): DecryptionConfig {
231
- // Load from environment variables
232
- const envConfig: Partial<DecryptionConfig> = {
233
- customKeyServerUrl: process.env.SEAL_KEY_SERVER_URL,
234
- customKeyServerObjectId: process.env.SEAL_KEY_SERVER_OBJECT_ID,
235
- sessionKeyTTL: process.env.SEAL_SESSION_TTL ? parseInt(process.env.SEAL_SESSION_TTL) : undefined,
236
- enableBatchDecryption: process.env.SEAL_ENABLE_BATCH === 'true',
237
- batchSize: process.env.SEAL_BATCH_SIZE ? parseInt(process.env.SEAL_BATCH_SIZE) : undefined,
238
- decryptionTimeout: process.env.SEAL_DECRYPTION_TIMEOUT ? parseInt(process.env.SEAL_DECRYPTION_TIMEOUT) : undefined,
239
- verifyKeyServers: process.env.SEAL_VERIFY_SERVERS !== 'false', // Default true
240
- enableDecryptionAudit: process.env.SEAL_ENABLE_AUDIT === 'true'
241
- };
242
-
243
- // Default configuration
244
- const defaults: DecryptionConfig = {
245
- keyServers: this.getDefaultKeyServers(),
246
- defaultKeyServerMode: 'open',
247
- sessionKeyTTL: 60, // 1 hour
248
- maxSessionKeys: 100,
249
- autoRefreshSession: true,
250
- enableBatchDecryption: true,
251
- batchSize: 10,
252
- maxConcurrentDecryptions: 5,
253
- decryptionTimeout: 30000, // 30 seconds
254
- enableFallback: true,
255
- maxRetryAttempts: 3,
256
- retryDelayMs: 1000,
257
- verifyKeyServers: process.env.NODE_ENV === 'production',
258
- enableDecryptionAudit: false,
259
- requireOwnershipVerification: true
260
- };
261
-
262
- return { ...defaults, ...envConfig, ...userConfig };
263
- }
264
-
265
- /**
266
- * Get default key servers based on environment
267
- */
268
- private getDefaultKeyServers(): KeyServerConfig[] {
269
- const network = process.env.SUI_NETWORK || 'testnet';
270
-
271
- if (network === 'testnet') {
272
- // Use official Mysten Labs servers as primary, with fallbacks
273
- return [
274
- ...MemoryDecryptionPipeline.DEFAULT_TESTNET_SERVERS,
275
- ...MemoryDecryptionPipeline.VERIFIED_TESTNET_SERVERS.slice(0, 2) // Add 2 fallback servers
276
- ];
277
- }
278
-
279
- // For mainnet, would need to configure based on chosen providers
280
- return MemoryDecryptionPipeline.DEFAULT_TESTNET_SERVERS;
281
- }
282
-
283
- /**
284
- * Initialize the decryption pipeline
285
- */
286
- private async initializeDecryptionPipeline(): Promise<void> {
287
- try {
288
- console.log('๐Ÿ”‘ Initializing SEAL Memory Decryption Pipeline...');
289
-
290
- // Determine key servers to use
291
- let keyServers = this.config.keyServers || this.getDefaultKeyServers();
292
-
293
- // Add custom key server if configured
294
- if (this.config.customKeyServerUrl && this.config.customKeyServerObjectId) {
295
- keyServers = [{
296
- name: 'custom-server',
297
- mode: this.config.defaultKeyServerMode || 'open',
298
- objectId: this.config.customKeyServerObjectId,
299
- url: this.config.customKeyServerUrl,
300
- provider: 'Custom',
301
- network: 'custom' as any,
302
- weight: 2, // Higher weight for custom server
303
- isDefault: false
304
- }, ...keyServers];
305
- }
306
-
307
- // Initialize SEAL client
308
- this.sealClient = new SealClient({
309
- suiClient: (this.encryptionService as any).suiClient,
310
- serverConfigs: keyServers.map(server => ({
311
- objectId: server.objectId,
312
- weight: server.weight || 1
313
- })),
314
- verifyKeyServers: this.config.verifyKeyServers || false
315
- });
316
-
317
- console.log(`โœ… SEAL client initialized with ${keyServers.length} key servers`);
318
- console.log('๐Ÿ“ก Key servers:', keyServers.map(s => `${s.name} (${s.provider})`).join(', '));
319
-
320
- // Start session key cleanup interval
321
- this.startSessionKeyCleanup();
322
-
323
- } catch (error) {
324
- console.error('โŒ Failed to initialize decryption pipeline:', error);
325
- throw new Error(`Decryption pipeline initialization failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
326
- }
327
- }
328
-
329
- // ==================== CORE DECRYPTION METHODS ====================
330
-
331
- /**
332
- * Decrypt a single memory with full pipeline support
333
- */
334
- async decryptMemory(request: DecryptionRequest): Promise<DecryptionResult> {
335
- const startTime = Date.now();
336
-
337
- try {
338
- // Check cache first
339
- const cacheKey = `${request.memoryId}:${request.userAddress}`;
340
- const cached = this.getFromCache(cacheKey);
341
- if (cached) {
342
- console.log(`๐ŸŽฏ Cache hit for memory ${request.memoryId}`);
343
- return {
344
- memoryId: request.memoryId,
345
- decryptedContent: cached,
346
- contentHash: request.contentHash || '',
347
- isVerified: true,
348
- decryptionTime: Date.now() - startTime,
349
- keyServerUsed: 'cache',
350
- sessionKeyId: 'cached'
351
- };
352
- }
353
-
354
- // Verify ownership if required
355
- if (this.config.requireOwnershipVerification && request.ownerAddress) {
356
- const hasAccess = await this.verifyAccess(request.userAddress, request.memoryId, request.ownerAddress);
357
- if (!hasAccess) {
358
- throw new Error(`Access denied: User ${request.userAddress} cannot decrypt memory ${request.memoryId}`);
359
- }
360
- }
361
-
362
- // Get or create session key
363
- const sessionKey = request.sessionKey || await this.getOrCreateSessionKey(request.userAddress);
364
-
365
- // Perform decryption
366
- const decryptedBytes = await this.performDecryption(request, sessionKey);
367
- const decryptedContent = new TextDecoder().decode(decryptedBytes);
368
-
369
- // Verify content integrity if hash provided
370
- let isVerified = true;
371
- if (request.contentHash) {
372
- isVerified = await this.verifyContentIntegrity(decryptedBytes, request.contentHash);
373
- if (!isVerified) {
374
- console.warn(`โš ๏ธ Content integrity check failed for memory ${request.memoryId}`);
375
- }
376
- }
377
-
378
- // Cache the result
379
- this.addToCache(cacheKey, decryptedContent);
380
-
381
- // Update stats
382
- this.updateDecryptionStats(true, Date.now() - startTime);
383
-
384
- const result: DecryptionResult = {
385
- memoryId: request.memoryId,
386
- decryptedContent,
387
- contentHash: request.contentHash || '',
388
- isVerified,
389
- decryptionTime: Date.now() - startTime,
390
- keyServerUsed: 'seal-client', // Would track specific server in production
391
- sessionKeyId: await this.getSessionKeyId(sessionKey)
392
- };
393
-
394
- console.log(`โœ… Successfully decrypted memory ${request.memoryId} in ${result.decryptionTime}ms`);
395
- return result;
396
-
397
- } catch (error) {
398
- this.updateDecryptionStats(false, Date.now() - startTime);
399
- const errorMsg = `Decryption failed for memory ${request.memoryId}: ${error instanceof Error ? error.message : 'Unknown error'}`;
400
- console.error('โŒ', errorMsg);
401
- throw new Error(errorMsg);
402
- }
403
- }
404
-
405
- /**
406
- * Decrypt multiple memories in batch for performance
407
- */
408
- async decryptMemoryBatch(requests: DecryptionRequest[]): Promise<BatchDecryptionResult> {
409
- console.log(`๐Ÿ”„ Starting batch decryption of ${requests.length} memories`);
410
- const startTime = Date.now();
411
-
412
- const successful: DecryptionResult[] = [];
413
- const failed: Array<{ memoryId: string; error: string; retryCount: number }> = [];
414
-
415
- // Group requests into batches
416
- const batchSize = this.config.batchSize || 10;
417
- const batches = this.chunkArray(requests, batchSize);
418
-
419
- for (const batch of batches) {
420
- // Process batch with concurrency limit
421
- const batchPromises = batch.map(async (request) => {
422
- try {
423
- const result = await this.decryptMemory(request);
424
- successful.push(result);
425
- } catch (error) {
426
- failed.push({
427
- memoryId: request.memoryId,
428
- error: error instanceof Error ? error.message : 'Unknown error',
429
- retryCount: 0
430
- });
431
- }
432
- });
433
-
434
- // Wait for batch to complete
435
- await Promise.allSettled(batchPromises);
436
- }
437
-
438
- // Retry failed decryptions if enabled
439
- if (this.config.enableFallback && this.config.maxRetryAttempts && this.config.maxRetryAttempts > 0) {
440
- await this.retryFailedDecryptions(requests, failed, successful);
441
- }
442
-
443
- const totalProcessingTime = Date.now() - startTime;
444
- const stats = this.generateBatchStats(requests.length, successful.length, failed.length, totalProcessingTime);
445
-
446
- console.log(`โœ… Batch decryption completed: ${successful.length}/${requests.length} successful in ${totalProcessingTime}ms`);
447
-
448
- return {
449
- successful,
450
- failed,
451
- stats
452
- };
453
- }
454
-
455
- // ==================== SESSION KEY MANAGEMENT ====================
456
-
457
- /**
458
- * Get or create session key for user
459
- */
460
- async getOrCreateSessionKey(userAddress: string): Promise<SessionKey> {
461
- // Check if we have a valid cached session key
462
- const existing = this.sessionKeys.get(userAddress);
463
- const timestamp = this.sessionKeyTimestamps.get(userAddress);
464
-
465
- if (existing && timestamp) {
466
- const age = Date.now() - timestamp;
467
- const ttl = (this.config.sessionKeyTTL || 60) * 60 * 1000; // Convert to ms
468
-
469
- if (age < ttl) {
470
- return existing;
471
- }
472
- }
473
-
474
- // Create new session key
475
- console.log(`๐Ÿ”‘ Creating new session key for user ${userAddress}`);
476
- const sessionKey = await this.encryptionService.createSessionKey(userAddress);
477
-
478
- // Cache with timestamp
479
- this.sessionKeys.set(userAddress, sessionKey);
480
- this.sessionKeyTimestamps.set(userAddress, Date.now());
481
-
482
- // Cleanup old keys if we exceed limit
483
- if (this.sessionKeys.size > (this.config.maxSessionKeys || 100)) {
484
- this.cleanupOldestSessionKeys();
485
- }
486
-
487
- return sessionKey;
488
- }
489
-
490
- /**
491
- * Cleanup expired session keys
492
- */
493
- private startSessionKeyCleanup(): void {
494
- setInterval(() => {
495
- const now = Date.now();
496
- const ttl = (this.config.sessionKeyTTL || 60) * 60 * 1000;
497
-
498
- for (const [userAddress, timestamp] of this.sessionKeyTimestamps.entries()) {
499
- if (now - timestamp > ttl) {
500
- this.sessionKeys.delete(userAddress);
501
- this.sessionKeyTimestamps.delete(userAddress);
502
- console.log(`๐Ÿงน Cleaned up expired session key for ${userAddress}`);
503
- }
504
- }
505
- }, 5 * 60 * 1000); // Check every 5 minutes
506
- }
507
-
508
- /**
509
- * Cleanup oldest session keys when limit exceeded
510
- */
511
- private cleanupOldestSessionKeys(): void {
512
- const entries = Array.from(this.sessionKeyTimestamps.entries())
513
- .sort((a, b) => a[1] - b[1]); // Sort by timestamp
514
-
515
- // Remove oldest 25%
516
- const toRemove = Math.floor(entries.length * 0.25);
517
- for (let i = 0; i < toRemove; i++) {
518
- const [userAddress] = entries[i];
519
- this.sessionKeys.delete(userAddress);
520
- this.sessionKeyTimestamps.delete(userAddress);
521
- }
522
-
523
- console.log(`๐Ÿงน Cleaned up ${toRemove} oldest session keys`);
524
- }
525
-
526
- // ==================== DECRYPTION UTILITIES ====================
527
-
528
- /**
529
- * Perform the actual decryption using SEAL
530
- */
531
- private async performDecryption(request: DecryptionRequest, sessionKey: SessionKey): Promise<Uint8Array> {
532
- if (!this.sealClient) {
533
- throw new Error('SEAL client not initialized');
534
- }
535
-
536
- // Convert base64 encrypted content to bytes
537
- const encryptedBytes = this.base64ToUint8Array(request.encryptedContent);
538
-
539
- // Build access transaction
540
- const accessTx = await this.encryptionService.buildAccessTransaction(request.userAddress, 'read');
541
- const txBytes = await accessTx.build({ client: (this.encryptionService as any).suiClient });
542
-
543
- // Perform decryption with timeout
544
- const decryptPromise = this.sealClient.decrypt({
545
- data: encryptedBytes,
546
- sessionKey: sessionKey,
547
- txBytes: txBytes,
548
- checkShareConsistency: true
549
- });
550
-
551
- // Add timeout
552
- const timeoutPromise = new Promise<never>((_, reject) => {
553
- setTimeout(() => reject(new Error('Decryption timeout')), this.config.decryptionTimeout || 30000);
554
- });
555
-
556
- return Promise.race([decryptPromise, timeoutPromise]);
557
- }
558
-
559
- /**
560
- * Verify user access to encrypted content
561
- */
562
- private async verifyAccess(userAddress: string, memoryId: string, ownerAddress: string): Promise<boolean> {
563
- try {
564
- return await this.encryptionService.hasAccess(userAddress, memoryId, ownerAddress);
565
- } catch (error) {
566
- console.error(`Access verification failed: ${error}`);
567
- return false;
568
- }
569
- }
570
-
571
- /**
572
- * Verify content integrity using hash
573
- */
574
- private async verifyContentIntegrity(content: Uint8Array, expectedHash: string): Promise<boolean> {
575
- try {
576
- return await (this.encryptionService as any).verifyContentHash(content, expectedHash);
577
- } catch (error) {
578
- console.error(`Content integrity verification failed: ${error}`);
579
- return false;
580
- }
581
- }
582
-
583
- /**
584
- * Retry failed decryptions with exponential backoff
585
- */
586
- private async retryFailedDecryptions(
587
- originalRequests: DecryptionRequest[],
588
- failed: Array<{ memoryId: string; error: string; retryCount: number }>,
589
- successful: DecryptionResult[]
590
- ): Promise<void> {
591
- const maxRetries = this.config.maxRetryAttempts || 3;
592
- const baseDelay = this.config.retryDelayMs || 1000;
593
-
594
- for (const failure of failed) {
595
- if (failure.retryCount >= maxRetries) continue;
596
-
597
- const originalRequest = originalRequests.find(r => r.memoryId === failure.memoryId);
598
- if (!originalRequest) continue;
599
-
600
- try {
601
- // Exponential backoff
602
- const delay = baseDelay * Math.pow(2, failure.retryCount);
603
- await new Promise(resolve => setTimeout(resolve, delay));
604
-
605
- console.log(`๐Ÿ”„ Retrying decryption for memory ${failure.memoryId} (attempt ${failure.retryCount + 1})`);
606
- const result = await this.decryptMemory(originalRequest);
607
-
608
- // Move from failed to successful
609
- successful.push(result);
610
- const failIndex = failed.indexOf(failure);
611
- failed.splice(failIndex, 1);
612
-
613
- console.log(`โœ… Retry successful for memory ${failure.memoryId}`);
614
- } catch (error) {
615
- failure.retryCount++;
616
- failure.error = error instanceof Error ? error.message : 'Unknown error';
617
- console.warn(`โš ๏ธ Retry ${failure.retryCount} failed for memory ${failure.memoryId}`);
618
- }
619
- }
620
- }
621
-
622
- // ==================== CACHE MANAGEMENT ====================
623
-
624
- private getFromCache(key: string): string | null {
625
- const cached = this.decryptionCache.get(key);
626
- if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) {
627
- return cached.content;
628
- }
629
- this.decryptionCache.delete(key);
630
- return null;
631
- }
632
-
633
- private addToCache(key: string, content: string): void {
634
- this.decryptionCache.set(key, { content, timestamp: Date.now() });
635
- }
636
-
637
- // ==================== UTILITY METHODS ====================
638
-
639
- private base64ToUint8Array(base64: string): Uint8Array {
640
- const binaryString = atob(base64);
641
- const bytes = new Uint8Array(binaryString.length);
642
- for (let i = 0; i < binaryString.length; i++) {
643
- bytes[i] = binaryString.charCodeAt(i);
644
- }
645
- return bytes;
646
- }
647
-
648
- private chunkArray<T>(array: T[], size: number): T[][] {
649
- const chunks: T[][] = [];
650
- for (let i = 0; i < array.length; i += size) {
651
- chunks.push(array.slice(i, i + size));
652
- }
653
- return chunks;
654
- }
655
-
656
- private async getSessionKeyId(sessionKey: SessionKey): Promise<string> {
657
- try {
658
- const exported = sessionKey.export();
659
- return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
660
- } catch {
661
- return `session_${Date.now()}`;
662
- }
663
- }
664
-
665
- private updateDecryptionStats(success: boolean, processingTime: number): void {
666
- this.decryptionStats.totalDecryptions++;
667
- this.decryptionStats.totalDecryptionTime += processingTime;
668
-
669
- if (success) {
670
- this.decryptionStats.successfulDecryptions++;
671
- } else {
672
- this.decryptionStats.failedDecryptions++;
673
- }
674
- }
675
-
676
- private generateBatchStats(
677
- totalRequests: number,
678
- successCount: number,
679
- failureCount: number,
680
- totalProcessingTime: number
681
- ): BatchDecryptionResult['stats'] {
682
- return {
683
- totalRequests,
684
- successCount,
685
- failureCount,
686
- totalProcessingTime,
687
- averageDecryptionTime: totalProcessingTime / totalRequests,
688
- keyServerPerformance: {} // Would be populated with actual server stats
689
- };
690
- }
691
-
692
- // ==================== PUBLIC API ====================
693
-
694
- /**
695
- * Decrypt encrypted content from unified memory results
696
- */
697
- async decryptMemoryResults(
698
- memories: UnifiedMemoryResult[],
699
- userAddress: string
700
- ): Promise<UnifiedMemoryResult[]> {
701
- console.log(`๐Ÿ” Decrypting ${memories.length} encrypted memories for user ${userAddress}`);
702
-
703
- const encryptedMemories = memories.filter(m => m.metadata.isEncrypted);
704
- if (encryptedMemories.length === 0) {
705
- console.log('๐Ÿ“‹ No encrypted memories found, returning original results');
706
- return memories;
707
- }
708
-
709
- // Prepare decryption requests
710
- const decryptionRequests: DecryptionRequest[] = encryptedMemories.map(memory => ({
711
- memoryId: memory.id,
712
- encryptedContent: memory.content || '',
713
- userAddress,
714
- ownerAddress: memory.metadata.owner,
715
- metadata: memory.metadata
716
- }));
717
-
718
- // Batch decrypt if enabled
719
- let decryptedResults: DecryptionResult[] = [];
720
- if (this.config.enableBatchDecryption && decryptionRequests.length > 1) {
721
- const batchResult = await this.decryptMemoryBatch(decryptionRequests);
722
- decryptedResults = batchResult.successful;
723
-
724
- if (batchResult.failed.length > 0) {
725
- console.warn(`โš ๏ธ Failed to decrypt ${batchResult.failed.length} memories:`,
726
- batchResult.failed.map(f => f.memoryId));
727
- }
728
- } else {
729
- // Decrypt individually
730
- for (const request of decryptionRequests) {
731
- try {
732
- const result = await this.decryptMemory(request);
733
- decryptedResults.push(result);
734
- } catch (error) {
735
- console.error(`Failed to decrypt memory ${request.memoryId}:`, error);
736
- }
737
- }
738
- }
739
-
740
- // Update memory results with decrypted content
741
- const decryptionMap = new Map(decryptedResults.map(r => [r.memoryId, r]));
742
-
743
- return memories.map(memory => {
744
- if (memory.metadata.isEncrypted && decryptionMap.has(memory.id)) {
745
- const decrypted = decryptionMap.get(memory.id)!;
746
- return {
747
- ...memory,
748
- content: decrypted.decryptedContent,
749
- analytics: {
750
- ...memory.analytics,
751
- viewCount: memory.analytics?.viewCount || 0,
752
- shareCount: memory.analytics?.shareCount || 0,
753
- editCount: memory.analytics?.editCount || 0,
754
- sentimentScore: memory.analytics?.sentimentScore || 0,
755
- topicDistribution: memory.analytics?.topicDistribution || {},
756
- decryptionTime: decrypted.decryptionTime,
757
- isDecryptionVerified: decrypted.isVerified
758
- }
759
- };
760
- }
761
- return memory;
762
- });
763
- }
764
-
765
- /**
766
- * Get decryption pipeline statistics
767
- */
768
- getDecryptionStats(): {
769
- totalDecryptions: number;
770
- successRate: number;
771
- averageDecryptionTime: number;
772
- cacheHitRate: number;
773
- activeSessionKeys: number;
774
- keyServerStatus: string[];
775
- } {
776
- const successRate = this.decryptionStats.totalDecryptions > 0
777
- ? this.decryptionStats.successfulDecryptions / this.decryptionStats.totalDecryptions
778
- : 0;
779
-
780
- const avgTime = this.decryptionStats.totalDecryptions > 0
781
- ? this.decryptionStats.totalDecryptionTime / this.decryptionStats.totalDecryptions
782
- : 0;
783
-
784
- return {
785
- totalDecryptions: this.decryptionStats.totalDecryptions,
786
- successRate,
787
- averageDecryptionTime: avgTime,
788
- cacheHitRate: 0.85, // Would calculate from actual cache stats
789
- activeSessionKeys: this.sessionKeys.size,
790
- keyServerStatus: this.config.keyServers?.map(s => s.name) || []
791
- };
792
- }
793
-
794
- /**
795
- * Clear decryption cache
796
- */
797
- clearCache(): void {
798
- this.decryptionCache.clear();
799
- console.log('๐Ÿงน Decryption cache cleared');
800
- }
801
-
802
- /**
803
- * Check if decryption pipeline is ready
804
- */
805
- isReady(): boolean {
806
- return this.sealClient !== null;
807
- }
808
-
809
- /**
810
- * Get pipeline configuration info
811
- */
812
- getConfigInfo(): {
813
- keyServers: number;
814
- defaultNetwork: string;
815
- batchingEnabled: boolean;
816
- cacheEnabled: boolean;
817
- } {
818
- return {
819
- keyServers: this.config.keyServers?.length || 0,
820
- defaultNetwork: process.env.SUI_NETWORK || 'testnet',
821
- batchingEnabled: this.config.enableBatchDecryption || false,
822
- cacheEnabled: true
823
- };
824
- }
1
+ /**
2
+ * MemoryDecryptionPipeline - Seamless SEAL-based Memory Decryption
3
+ *
4
+ * Provides comprehensive decryption capabilities for encrypted memories using
5
+ * Mysten's SEAL SDK with official testnet key servers and configurable infrastructure.
6
+ *
7
+ * Features:
8
+ * - ๐Ÿ” Seamless SEAL decryption integration
9
+ * - ๐Ÿ”‘ Automatic key server configuration (testnet/mainnet)
10
+ * - โšก Batch decryption optimization
11
+ * - ๐Ÿ›ก๏ธ Secure session key management
12
+ * - ๐Ÿ”„ Automatic retry and fallback mechanisms
13
+ * - ๐Ÿ“Š Decryption analytics and monitoring
14
+ * - ๐ŸŒ Environment-based configuration
15
+ */
16
+
17
+ import { SealClient, SessionKey } from '@mysten/seal';
18
+ import { Transaction } from '@mysten/sui/transactions';
19
+ import { fromHex, toHex } from '@mysten/sui/utils';
20
+ import { EncryptionService } from '../services/EncryptionService';
21
+ import { StorageManager } from '../infrastructure/walrus/StorageManager';
22
+ import { UnifiedMemoryResult } from '../retrieval/MemoryRetrievalService';
23
+
24
+ // Key server configurations from SEAL documentation
25
+ export interface KeyServerConfig {
26
+ name: string;
27
+ mode: 'open' | 'permissioned';
28
+ objectId: string;
29
+ url: string;
30
+ provider: string;
31
+ network: 'testnet' | 'mainnet';
32
+ weight?: number;
33
+ isDefault?: boolean;
34
+ }
35
+
36
+ export interface DecryptionConfig {
37
+ // Key server configuration
38
+ keyServers?: KeyServerConfig[];
39
+ defaultKeyServerMode?: 'open' | 'permissioned';
40
+ customKeyServerUrl?: string;
41
+ customKeyServerObjectId?: string;
42
+
43
+ // Session management
44
+ sessionKeyTTL?: number; // minutes
45
+ maxSessionKeys?: number;
46
+ autoRefreshSession?: boolean;
47
+
48
+ // Performance options
49
+ enableBatchDecryption?: boolean;
50
+ batchSize?: number;
51
+ maxConcurrentDecryptions?: number;
52
+ decryptionTimeout?: number; // ms
53
+
54
+ // Fallback options
55
+ enableFallback?: boolean;
56
+ maxRetryAttempts?: number;
57
+ retryDelayMs?: number;
58
+
59
+ // Security options
60
+ verifyKeyServers?: boolean;
61
+ enableDecryptionAudit?: boolean;
62
+ requireOwnershipVerification?: boolean;
63
+ }
64
+
65
+ export interface DecryptionRequest {
66
+ memoryId: string;
67
+ encryptedContent: string;
68
+ contentHash?: string;
69
+ userAddress: string;
70
+ ownerAddress?: string;
71
+ sessionKey?: SessionKey;
72
+ metadata?: Record<string, any>;
73
+ }
74
+
75
+ export interface DecryptionResult {
76
+ memoryId: string;
77
+ decryptedContent: string;
78
+ contentHash: string;
79
+ isVerified: boolean;
80
+ decryptionTime: number;
81
+ keyServerUsed: string;
82
+ sessionKeyId: string;
83
+ }
84
+
85
+ export interface BatchDecryptionResult {
86
+ successful: DecryptionResult[];
87
+ failed: Array<{
88
+ memoryId: string;
89
+ error: string;
90
+ retryCount: number;
91
+ }>;
92
+ stats: {
93
+ totalRequests: number;
94
+ successCount: number;
95
+ failureCount: number;
96
+ totalProcessingTime: number;
97
+ averageDecryptionTime: number;
98
+ keyServerPerformance: Record<string, {
99
+ requests: number;
100
+ successes: number;
101
+ averageTime: number;
102
+ }>;
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Memory Decryption Pipeline Service
108
+ */
109
+ export class MemoryDecryptionPipeline {
110
+ private sealClient: SealClient | null = null;
111
+ private encryptionService: EncryptionService;
112
+ private storageManager: StorageManager;
113
+ private config: DecryptionConfig;
114
+
115
+ // Session key management
116
+ private sessionKeys = new Map<string, SessionKey>();
117
+ private sessionKeyTimestamps = new Map<string, number>();
118
+
119
+ // Decryption cache
120
+ private decryptionCache = new Map<string, { content: string; timestamp: number }>();
121
+ private readonly CACHE_TTL = 30 * 60 * 1000; // 30 minutes
122
+
123
+ // Performance monitoring
124
+ private decryptionStats = {
125
+ totalDecryptions: 0,
126
+ successfulDecryptions: 0,
127
+ failedDecryptions: 0,
128
+ totalDecryptionTime: 0,
129
+ keyServerStats: new Map<string, { requests: number; successes: number; totalTime: number }>()
130
+ };
131
+
132
+ // Official Mysten Labs testnet key servers from documentation
133
+ private static readonly DEFAULT_TESTNET_SERVERS: KeyServerConfig[] = [
134
+ {
135
+ name: 'mysten-testnet-1',
136
+ mode: 'open',
137
+ objectId: '0x73d05d62c18d9374e3ea529e8e0ed6161da1a141a94d3f76ae3fe4e99356db75',
138
+ url: 'https://seal-key-server-testnet-1.mystenlabs.com',
139
+ provider: 'Mysten Labs',
140
+ network: 'testnet',
141
+ weight: 1,
142
+ isDefault: true
143
+ },
144
+ {
145
+ name: 'mysten-testnet-2',
146
+ mode: 'open',
147
+ objectId: '0xf5d14a81a982144ae441cd7d64b09027f116a468bd36e7eca494f750591623c8',
148
+ url: 'https://seal-key-server-testnet-2.mystenlabs.com',
149
+ provider: 'Mysten Labs',
150
+ network: 'testnet',
151
+ weight: 1,
152
+ isDefault: true
153
+ }
154
+ ];
155
+
156
+ // Additional verified testnet key servers
157
+ private static readonly VERIFIED_TESTNET_SERVERS: KeyServerConfig[] = [
158
+ {
159
+ name: 'ruby-nodes-testnet',
160
+ mode: 'open',
161
+ objectId: '0x6068c0acb197dddbacd4746a9de7f025b2ed5a5b6c1b1ab44dade4426d141da2',
162
+ url: 'https://seal-testnet.api.rubynodes.io',
163
+ provider: 'Ruby Nodes',
164
+ network: 'testnet',
165
+ weight: 1
166
+ },
167
+ {
168
+ name: 'nodeinfra-testnet',
169
+ mode: 'open',
170
+ objectId: '0x5466b7df5c15b508678d51496ada8afab0d6f70a01c10613123382b1b8131007',
171
+ url: 'https://open-seal-testnet.nodeinfra.com',
172
+ provider: 'NodeInfra',
173
+ network: 'testnet',
174
+ weight: 1
175
+ },
176
+ {
177
+ name: 'studio-mirai-testnet',
178
+ mode: 'open',
179
+ objectId: '0x164ac3d2b3b8694b8181c13f671950004765c23f270321a45fdd04d40cccf0f2',
180
+ url: 'https://open.key-server-testnet.seal.mirai.cloud',
181
+ provider: 'Studio Mirai',
182
+ network: 'testnet',
183
+ weight: 1
184
+ },
185
+ {
186
+ name: 'overclock-testnet',
187
+ mode: 'open',
188
+ objectId: '0x9c949e53c36ab7a9c484ed9e8b43267a77d4b8d70e79aa6b39042e3d4c434105',
189
+ url: 'https://seal-testnet-open.overclock.run',
190
+ provider: 'Overclock',
191
+ network: 'testnet',
192
+ weight: 1
193
+ },
194
+ {
195
+ name: 'h2o-nodes-testnet',
196
+ mode: 'open',
197
+ objectId: '0x39cef09b24b667bc6ed54f7159d82352fe2d5dd97ca9a5beaa1d21aa774f25a2',
198
+ url: 'https://seal-open.sui-testnet.h2o-nodes.com',
199
+ provider: 'H2O Nodes',
200
+ network: 'testnet',
201
+ weight: 1
202
+ },
203
+ {
204
+ name: 'triton-one-testnet',
205
+ mode: 'open',
206
+ objectId: '0x4cded1abeb52a22b6becb42a91d3686a4c901cf52eee16234214d0b5b2da4c46',
207
+ url: 'https://seal.testnet.sui.rpcpool.com',
208
+ provider: 'Triton One',
209
+ network: 'testnet',
210
+ weight: 1
211
+ }
212
+ ];
213
+
214
+ constructor(
215
+ encryptionService: EncryptionService,
216
+ storageManager: StorageManager,
217
+ config?: Partial<DecryptionConfig>
218
+ ) {
219
+ this.encryptionService = encryptionService;
220
+ this.storageManager = storageManager;
221
+ this.config = this.mergeWithDefaults(config || {});
222
+ this.initializeDecryptionPipeline();
223
+ }
224
+
225
+ // ==================== INITIALIZATION ====================
226
+
227
+ /**
228
+ * Merge user config with environment variables and defaults
229
+ */
230
+ private mergeWithDefaults(userConfig: Partial<DecryptionConfig>): DecryptionConfig {
231
+ // Load from environment variables
232
+ const envConfig: Partial<DecryptionConfig> = {
233
+ customKeyServerUrl: process.env.SEAL_KEY_SERVER_URL,
234
+ customKeyServerObjectId: process.env.SEAL_KEY_SERVER_OBJECT_ID,
235
+ sessionKeyTTL: process.env.SEAL_SESSION_TTL ? parseInt(process.env.SEAL_SESSION_TTL) : undefined,
236
+ enableBatchDecryption: process.env.SEAL_ENABLE_BATCH === 'true',
237
+ batchSize: process.env.SEAL_BATCH_SIZE ? parseInt(process.env.SEAL_BATCH_SIZE) : undefined,
238
+ decryptionTimeout: process.env.SEAL_DECRYPTION_TIMEOUT ? parseInt(process.env.SEAL_DECRYPTION_TIMEOUT) : undefined,
239
+ verifyKeyServers: process.env.SEAL_VERIFY_SERVERS !== 'false', // Default true
240
+ enableDecryptionAudit: process.env.SEAL_ENABLE_AUDIT === 'true'
241
+ };
242
+
243
+ // Default configuration
244
+ const defaults: DecryptionConfig = {
245
+ keyServers: this.getDefaultKeyServers(),
246
+ defaultKeyServerMode: 'open',
247
+ sessionKeyTTL: 60, // 1 hour
248
+ maxSessionKeys: 100,
249
+ autoRefreshSession: true,
250
+ enableBatchDecryption: true,
251
+ batchSize: 10,
252
+ maxConcurrentDecryptions: 5,
253
+ decryptionTimeout: 30000, // 30 seconds
254
+ enableFallback: true,
255
+ maxRetryAttempts: 3,
256
+ retryDelayMs: 1000,
257
+ verifyKeyServers: process.env.NODE_ENV === 'production',
258
+ enableDecryptionAudit: false,
259
+ requireOwnershipVerification: true
260
+ };
261
+
262
+ return { ...defaults, ...envConfig, ...userConfig };
263
+ }
264
+
265
+ /**
266
+ * Get default key servers based on environment
267
+ */
268
+ private getDefaultKeyServers(): KeyServerConfig[] {
269
+ const network = process.env.SUI_NETWORK || 'testnet';
270
+
271
+ if (network === 'testnet') {
272
+ // Use official Mysten Labs servers as primary, with fallbacks
273
+ return [
274
+ ...MemoryDecryptionPipeline.DEFAULT_TESTNET_SERVERS,
275
+ ...MemoryDecryptionPipeline.VERIFIED_TESTNET_SERVERS.slice(0, 2) // Add 2 fallback servers
276
+ ];
277
+ }
278
+
279
+ // For mainnet, would need to configure based on chosen providers
280
+ return MemoryDecryptionPipeline.DEFAULT_TESTNET_SERVERS;
281
+ }
282
+
283
+ /**
284
+ * Initialize the decryption pipeline
285
+ */
286
+ private async initializeDecryptionPipeline(): Promise<void> {
287
+ try {
288
+ console.log('๐Ÿ”‘ Initializing SEAL Memory Decryption Pipeline...');
289
+
290
+ // Determine key servers to use
291
+ let keyServers = this.config.keyServers || this.getDefaultKeyServers();
292
+
293
+ // Add custom key server if configured
294
+ if (this.config.customKeyServerUrl && this.config.customKeyServerObjectId) {
295
+ keyServers = [{
296
+ name: 'custom-server',
297
+ mode: this.config.defaultKeyServerMode || 'open',
298
+ objectId: this.config.customKeyServerObjectId,
299
+ url: this.config.customKeyServerUrl,
300
+ provider: 'Custom',
301
+ network: 'custom' as any,
302
+ weight: 2, // Higher weight for custom server
303
+ isDefault: false
304
+ }, ...keyServers];
305
+ }
306
+
307
+ // Initialize SEAL client
308
+ this.sealClient = new SealClient({
309
+ suiClient: (this.encryptionService as any).suiClient,
310
+ serverConfigs: keyServers.map(server => ({
311
+ objectId: server.objectId,
312
+ weight: server.weight || 1
313
+ })),
314
+ verifyKeyServers: this.config.verifyKeyServers || false
315
+ });
316
+
317
+ console.log(`โœ… SEAL client initialized with ${keyServers.length} key servers`);
318
+ console.log('๐Ÿ“ก Key servers:', keyServers.map(s => `${s.name} (${s.provider})`).join(', '));
319
+
320
+ // Start session key cleanup interval
321
+ this.startSessionKeyCleanup();
322
+
323
+ } catch (error) {
324
+ console.error('โŒ Failed to initialize decryption pipeline:', error);
325
+ throw new Error(`Decryption pipeline initialization failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
326
+ }
327
+ }
328
+
329
+ // ==================== CORE DECRYPTION METHODS ====================
330
+
331
+ /**
332
+ * Decrypt a single memory with full pipeline support
333
+ */
334
+ async decryptMemory(request: DecryptionRequest): Promise<DecryptionResult> {
335
+ const startTime = Date.now();
336
+
337
+ try {
338
+ // Check cache first
339
+ const cacheKey = `${request.memoryId}:${request.userAddress}`;
340
+ const cached = this.getFromCache(cacheKey);
341
+ if (cached) {
342
+ console.log(`๐ŸŽฏ Cache hit for memory ${request.memoryId}`);
343
+ return {
344
+ memoryId: request.memoryId,
345
+ decryptedContent: cached,
346
+ contentHash: request.contentHash || '',
347
+ isVerified: true,
348
+ decryptionTime: Date.now() - startTime,
349
+ keyServerUsed: 'cache',
350
+ sessionKeyId: 'cached'
351
+ };
352
+ }
353
+
354
+ // Verify ownership if required
355
+ if (this.config.requireOwnershipVerification && request.ownerAddress) {
356
+ const hasAccess = await this.verifyAccess(request.userAddress, request.memoryId, request.ownerAddress);
357
+ if (!hasAccess) {
358
+ throw new Error(`Access denied: User ${request.userAddress} cannot decrypt memory ${request.memoryId}`);
359
+ }
360
+ }
361
+
362
+ // Get or create session key
363
+ const sessionKey = request.sessionKey || await this.getOrCreateSessionKey(request.userAddress);
364
+
365
+ // Perform decryption
366
+ const decryptedBytes = await this.performDecryption(request, sessionKey);
367
+ const decryptedContent = new TextDecoder().decode(decryptedBytes);
368
+
369
+ // Verify content integrity if hash provided
370
+ let isVerified = true;
371
+ if (request.contentHash) {
372
+ isVerified = await this.verifyContentIntegrity(decryptedBytes, request.contentHash);
373
+ if (!isVerified) {
374
+ console.warn(`โš ๏ธ Content integrity check failed for memory ${request.memoryId}`);
375
+ }
376
+ }
377
+
378
+ // Cache the result
379
+ this.addToCache(cacheKey, decryptedContent);
380
+
381
+ // Update stats
382
+ this.updateDecryptionStats(true, Date.now() - startTime);
383
+
384
+ const result: DecryptionResult = {
385
+ memoryId: request.memoryId,
386
+ decryptedContent,
387
+ contentHash: request.contentHash || '',
388
+ isVerified,
389
+ decryptionTime: Date.now() - startTime,
390
+ keyServerUsed: 'seal-client', // Would track specific server in production
391
+ sessionKeyId: await this.getSessionKeyId(sessionKey)
392
+ };
393
+
394
+ console.log(`โœ… Successfully decrypted memory ${request.memoryId} in ${result.decryptionTime}ms`);
395
+ return result;
396
+
397
+ } catch (error) {
398
+ this.updateDecryptionStats(false, Date.now() - startTime);
399
+ const errorMsg = `Decryption failed for memory ${request.memoryId}: ${error instanceof Error ? error.message : 'Unknown error'}`;
400
+ console.error('โŒ', errorMsg);
401
+ throw new Error(errorMsg);
402
+ }
403
+ }
404
+
405
+ /**
406
+ * Decrypt multiple memories in batch for performance
407
+ */
408
+ async decryptMemoryBatch(requests: DecryptionRequest[]): Promise<BatchDecryptionResult> {
409
+ console.log(`๐Ÿ”„ Starting batch decryption of ${requests.length} memories`);
410
+ const startTime = Date.now();
411
+
412
+ const successful: DecryptionResult[] = [];
413
+ const failed: Array<{ memoryId: string; error: string; retryCount: number }> = [];
414
+
415
+ // Group requests into batches
416
+ const batchSize = this.config.batchSize || 10;
417
+ const batches = this.chunkArray(requests, batchSize);
418
+
419
+ for (const batch of batches) {
420
+ // Process batch with concurrency limit
421
+ const batchPromises = batch.map(async (request) => {
422
+ try {
423
+ const result = await this.decryptMemory(request);
424
+ successful.push(result);
425
+ } catch (error) {
426
+ failed.push({
427
+ memoryId: request.memoryId,
428
+ error: error instanceof Error ? error.message : 'Unknown error',
429
+ retryCount: 0
430
+ });
431
+ }
432
+ });
433
+
434
+ // Wait for batch to complete
435
+ await Promise.allSettled(batchPromises);
436
+ }
437
+
438
+ // Retry failed decryptions if enabled
439
+ if (this.config.enableFallback && this.config.maxRetryAttempts && this.config.maxRetryAttempts > 0) {
440
+ await this.retryFailedDecryptions(requests, failed, successful);
441
+ }
442
+
443
+ const totalProcessingTime = Date.now() - startTime;
444
+ const stats = this.generateBatchStats(requests.length, successful.length, failed.length, totalProcessingTime);
445
+
446
+ console.log(`โœ… Batch decryption completed: ${successful.length}/${requests.length} successful in ${totalProcessingTime}ms`);
447
+
448
+ return {
449
+ successful,
450
+ failed,
451
+ stats
452
+ };
453
+ }
454
+
455
+ // ==================== SESSION KEY MANAGEMENT ====================
456
+
457
+ /**
458
+ * Get or create session key for user
459
+ */
460
+ async getOrCreateSessionKey(userAddress: string): Promise<SessionKey> {
461
+ // Check if we have a valid cached session key
462
+ const existing = this.sessionKeys.get(userAddress);
463
+ const timestamp = this.sessionKeyTimestamps.get(userAddress);
464
+
465
+ if (existing && timestamp) {
466
+ const age = Date.now() - timestamp;
467
+ const ttl = (this.config.sessionKeyTTL || 60) * 60 * 1000; // Convert to ms
468
+
469
+ if (age < ttl) {
470
+ return existing;
471
+ }
472
+ }
473
+
474
+ // Create new session key
475
+ console.log(`๐Ÿ”‘ Creating new session key for user ${userAddress}`);
476
+ const sessionKey = await this.encryptionService.createSessionKey(userAddress);
477
+
478
+ // Cache with timestamp
479
+ this.sessionKeys.set(userAddress, sessionKey);
480
+ this.sessionKeyTimestamps.set(userAddress, Date.now());
481
+
482
+ // Cleanup old keys if we exceed limit
483
+ if (this.sessionKeys.size > (this.config.maxSessionKeys || 100)) {
484
+ this.cleanupOldestSessionKeys();
485
+ }
486
+
487
+ return sessionKey;
488
+ }
489
+
490
+ /**
491
+ * Cleanup expired session keys
492
+ */
493
+ private startSessionKeyCleanup(): void {
494
+ setInterval(() => {
495
+ const now = Date.now();
496
+ const ttl = (this.config.sessionKeyTTL || 60) * 60 * 1000;
497
+
498
+ for (const [userAddress, timestamp] of this.sessionKeyTimestamps.entries()) {
499
+ if (now - timestamp > ttl) {
500
+ this.sessionKeys.delete(userAddress);
501
+ this.sessionKeyTimestamps.delete(userAddress);
502
+ console.log(`๐Ÿงน Cleaned up expired session key for ${userAddress}`);
503
+ }
504
+ }
505
+ }, 5 * 60 * 1000); // Check every 5 minutes
506
+ }
507
+
508
+ /**
509
+ * Cleanup oldest session keys when limit exceeded
510
+ */
511
+ private cleanupOldestSessionKeys(): void {
512
+ const entries = Array.from(this.sessionKeyTimestamps.entries())
513
+ .sort((a, b) => a[1] - b[1]); // Sort by timestamp
514
+
515
+ // Remove oldest 25%
516
+ const toRemove = Math.floor(entries.length * 0.25);
517
+ for (let i = 0; i < toRemove; i++) {
518
+ const [userAddress] = entries[i];
519
+ this.sessionKeys.delete(userAddress);
520
+ this.sessionKeyTimestamps.delete(userAddress);
521
+ }
522
+
523
+ console.log(`๐Ÿงน Cleaned up ${toRemove} oldest session keys`);
524
+ }
525
+
526
+ // ==================== DECRYPTION UTILITIES ====================
527
+
528
+ /**
529
+ * Perform the actual decryption using SEAL
530
+ */
531
+ private async performDecryption(request: DecryptionRequest, sessionKey: SessionKey): Promise<Uint8Array> {
532
+ if (!this.sealClient) {
533
+ throw new Error('SEAL client not initialized');
534
+ }
535
+
536
+ // Convert base64 encrypted content to bytes
537
+ const encryptedBytes = this.base64ToUint8Array(request.encryptedContent);
538
+
539
+ // Build access transaction
540
+ const accessTx = await this.encryptionService.buildAccessTransaction(request.userAddress, 'read');
541
+ // SEAL REQUIREMENT: Must use onlyTransactionKind: true for PTB validation
542
+ const txBytes = await accessTx.build({ client: (this.encryptionService as any).suiClient, onlyTransactionKind: true });
543
+
544
+ // Perform decryption with timeout
545
+ const decryptPromise = this.sealClient.decrypt({
546
+ data: encryptedBytes,
547
+ sessionKey: sessionKey,
548
+ txBytes: txBytes,
549
+ checkShareConsistency: true
550
+ });
551
+
552
+ // Add timeout
553
+ const timeoutPromise = new Promise<never>((_, reject) => {
554
+ setTimeout(() => reject(new Error('Decryption timeout')), this.config.decryptionTimeout || 30000);
555
+ });
556
+
557
+ return Promise.race([decryptPromise, timeoutPromise]);
558
+ }
559
+
560
+ /**
561
+ * Verify user access to encrypted content
562
+ */
563
+ private async verifyAccess(userAddress: string, memoryId: string, ownerAddress: string): Promise<boolean> {
564
+ try {
565
+ return await this.encryptionService.hasAccess(userAddress, memoryId, ownerAddress);
566
+ } catch (error) {
567
+ console.error(`Access verification failed: ${error}`);
568
+ return false;
569
+ }
570
+ }
571
+
572
+ /**
573
+ * Verify content integrity using hash
574
+ */
575
+ private async verifyContentIntegrity(content: Uint8Array, expectedHash: string): Promise<boolean> {
576
+ try {
577
+ return await (this.encryptionService as any).verifyContentHash(content, expectedHash);
578
+ } catch (error) {
579
+ console.error(`Content integrity verification failed: ${error}`);
580
+ return false;
581
+ }
582
+ }
583
+
584
+ /**
585
+ * Retry failed decryptions with exponential backoff
586
+ */
587
+ private async retryFailedDecryptions(
588
+ originalRequests: DecryptionRequest[],
589
+ failed: Array<{ memoryId: string; error: string; retryCount: number }>,
590
+ successful: DecryptionResult[]
591
+ ): Promise<void> {
592
+ const maxRetries = this.config.maxRetryAttempts || 3;
593
+ const baseDelay = this.config.retryDelayMs || 1000;
594
+
595
+ for (const failure of failed) {
596
+ if (failure.retryCount >= maxRetries) continue;
597
+
598
+ const originalRequest = originalRequests.find(r => r.memoryId === failure.memoryId);
599
+ if (!originalRequest) continue;
600
+
601
+ try {
602
+ // Exponential backoff
603
+ const delay = baseDelay * Math.pow(2, failure.retryCount);
604
+ await new Promise(resolve => setTimeout(resolve, delay));
605
+
606
+ console.log(`๐Ÿ”„ Retrying decryption for memory ${failure.memoryId} (attempt ${failure.retryCount + 1})`);
607
+ const result = await this.decryptMemory(originalRequest);
608
+
609
+ // Move from failed to successful
610
+ successful.push(result);
611
+ const failIndex = failed.indexOf(failure);
612
+ failed.splice(failIndex, 1);
613
+
614
+ console.log(`โœ… Retry successful for memory ${failure.memoryId}`);
615
+ } catch (error) {
616
+ failure.retryCount++;
617
+ failure.error = error instanceof Error ? error.message : 'Unknown error';
618
+ console.warn(`โš ๏ธ Retry ${failure.retryCount} failed for memory ${failure.memoryId}`);
619
+ }
620
+ }
621
+ }
622
+
623
+ // ==================== CACHE MANAGEMENT ====================
624
+
625
+ private getFromCache(key: string): string | null {
626
+ const cached = this.decryptionCache.get(key);
627
+ if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) {
628
+ return cached.content;
629
+ }
630
+ this.decryptionCache.delete(key);
631
+ return null;
632
+ }
633
+
634
+ private addToCache(key: string, content: string): void {
635
+ this.decryptionCache.set(key, { content, timestamp: Date.now() });
636
+ }
637
+
638
+ // ==================== UTILITY METHODS ====================
639
+
640
+ private base64ToUint8Array(base64: string): Uint8Array {
641
+ const binaryString = atob(base64);
642
+ const bytes = new Uint8Array(binaryString.length);
643
+ for (let i = 0; i < binaryString.length; i++) {
644
+ bytes[i] = binaryString.charCodeAt(i);
645
+ }
646
+ return bytes;
647
+ }
648
+
649
+ private chunkArray<T>(array: T[], size: number): T[][] {
650
+ const chunks: T[][] = [];
651
+ for (let i = 0; i < array.length; i += size) {
652
+ chunks.push(array.slice(i, i + size));
653
+ }
654
+ return chunks;
655
+ }
656
+
657
+ private async getSessionKeyId(sessionKey: SessionKey): Promise<string> {
658
+ try {
659
+ const exported = sessionKey.export();
660
+ return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
661
+ } catch {
662
+ return `session_${Date.now()}`;
663
+ }
664
+ }
665
+
666
+ private updateDecryptionStats(success: boolean, processingTime: number): void {
667
+ this.decryptionStats.totalDecryptions++;
668
+ this.decryptionStats.totalDecryptionTime += processingTime;
669
+
670
+ if (success) {
671
+ this.decryptionStats.successfulDecryptions++;
672
+ } else {
673
+ this.decryptionStats.failedDecryptions++;
674
+ }
675
+ }
676
+
677
+ private generateBatchStats(
678
+ totalRequests: number,
679
+ successCount: number,
680
+ failureCount: number,
681
+ totalProcessingTime: number
682
+ ): BatchDecryptionResult['stats'] {
683
+ return {
684
+ totalRequests,
685
+ successCount,
686
+ failureCount,
687
+ totalProcessingTime,
688
+ averageDecryptionTime: totalProcessingTime / totalRequests,
689
+ keyServerPerformance: {} // Would be populated with actual server stats
690
+ };
691
+ }
692
+
693
+ // ==================== PUBLIC API ====================
694
+
695
+ /**
696
+ * Decrypt encrypted content from unified memory results
697
+ */
698
+ async decryptMemoryResults(
699
+ memories: UnifiedMemoryResult[],
700
+ userAddress: string
701
+ ): Promise<UnifiedMemoryResult[]> {
702
+ console.log(`๐Ÿ” Decrypting ${memories.length} encrypted memories for user ${userAddress}`);
703
+
704
+ const encryptedMemories = memories.filter(m => m.metadata.isEncrypted);
705
+ if (encryptedMemories.length === 0) {
706
+ console.log('๐Ÿ“‹ No encrypted memories found, returning original results');
707
+ return memories;
708
+ }
709
+
710
+ // Prepare decryption requests
711
+ const decryptionRequests: DecryptionRequest[] = encryptedMemories.map(memory => ({
712
+ memoryId: memory.id,
713
+ encryptedContent: memory.content || '',
714
+ userAddress,
715
+ ownerAddress: memory.metadata.owner,
716
+ metadata: memory.metadata
717
+ }));
718
+
719
+ // Batch decrypt if enabled
720
+ let decryptedResults: DecryptionResult[] = [];
721
+ if (this.config.enableBatchDecryption && decryptionRequests.length > 1) {
722
+ const batchResult = await this.decryptMemoryBatch(decryptionRequests);
723
+ decryptedResults = batchResult.successful;
724
+
725
+ if (batchResult.failed.length > 0) {
726
+ console.warn(`โš ๏ธ Failed to decrypt ${batchResult.failed.length} memories:`,
727
+ batchResult.failed.map(f => f.memoryId));
728
+ }
729
+ } else {
730
+ // Decrypt individually
731
+ for (const request of decryptionRequests) {
732
+ try {
733
+ const result = await this.decryptMemory(request);
734
+ decryptedResults.push(result);
735
+ } catch (error) {
736
+ console.error(`Failed to decrypt memory ${request.memoryId}:`, error);
737
+ }
738
+ }
739
+ }
740
+
741
+ // Update memory results with decrypted content
742
+ const decryptionMap = new Map(decryptedResults.map(r => [r.memoryId, r]));
743
+
744
+ return memories.map(memory => {
745
+ if (memory.metadata.isEncrypted && decryptionMap.has(memory.id)) {
746
+ const decrypted = decryptionMap.get(memory.id)!;
747
+ return {
748
+ ...memory,
749
+ content: decrypted.decryptedContent,
750
+ analytics: {
751
+ ...memory.analytics,
752
+ viewCount: memory.analytics?.viewCount || 0,
753
+ shareCount: memory.analytics?.shareCount || 0,
754
+ editCount: memory.analytics?.editCount || 0,
755
+ sentimentScore: memory.analytics?.sentimentScore || 0,
756
+ topicDistribution: memory.analytics?.topicDistribution || {},
757
+ decryptionTime: decrypted.decryptionTime,
758
+ isDecryptionVerified: decrypted.isVerified
759
+ }
760
+ };
761
+ }
762
+ return memory;
763
+ });
764
+ }
765
+
766
+ /**
767
+ * Get decryption pipeline statistics
768
+ */
769
+ getDecryptionStats(): {
770
+ totalDecryptions: number;
771
+ successRate: number;
772
+ averageDecryptionTime: number;
773
+ cacheHitRate: number;
774
+ activeSessionKeys: number;
775
+ keyServerStatus: string[];
776
+ } {
777
+ const successRate = this.decryptionStats.totalDecryptions > 0
778
+ ? this.decryptionStats.successfulDecryptions / this.decryptionStats.totalDecryptions
779
+ : 0;
780
+
781
+ const avgTime = this.decryptionStats.totalDecryptions > 0
782
+ ? this.decryptionStats.totalDecryptionTime / this.decryptionStats.totalDecryptions
783
+ : 0;
784
+
785
+ return {
786
+ totalDecryptions: this.decryptionStats.totalDecryptions,
787
+ successRate,
788
+ averageDecryptionTime: avgTime,
789
+ cacheHitRate: 0.85, // Would calculate from actual cache stats
790
+ activeSessionKeys: this.sessionKeys.size,
791
+ keyServerStatus: this.config.keyServers?.map(s => s.name) || []
792
+ };
793
+ }
794
+
795
+ /**
796
+ * Clear decryption cache
797
+ */
798
+ clearCache(): void {
799
+ this.decryptionCache.clear();
800
+ console.log('๐Ÿงน Decryption cache cleared');
801
+ }
802
+
803
+ /**
804
+ * Check if decryption pipeline is ready
805
+ */
806
+ isReady(): boolean {
807
+ return this.sealClient !== null;
808
+ }
809
+
810
+ /**
811
+ * Get pipeline configuration info
812
+ */
813
+ getConfigInfo(): {
814
+ keyServers: number;
815
+ defaultNetwork: string;
816
+ batchingEnabled: boolean;
817
+ cacheEnabled: boolean;
818
+ } {
819
+ return {
820
+ keyServers: this.config.keyServers?.length || 0,
821
+ defaultNetwork: process.env.SUI_NETWORK || 'testnet',
822
+ batchingEnabled: this.config.enableBatchDecryption || false,
823
+ cacheEnabled: true
824
+ };
825
+ }
825
826
  }