@cofhe/sdk 0.1.1 → 0.2.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 (96) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/adapters/ethers6.ts +28 -28
  3. package/adapters/hardhat.ts +0 -1
  4. package/adapters/index.test.ts +14 -19
  5. package/adapters/smartWallet.ts +81 -73
  6. package/adapters/test-utils.ts +45 -45
  7. package/adapters/types.ts +3 -3
  8. package/chains/chains/localcofhe.ts +14 -0
  9. package/chains/chains.test.ts +2 -1
  10. package/chains/index.ts +3 -1
  11. package/core/baseBuilder.ts +30 -49
  12. package/core/client.test.ts +94 -77
  13. package/core/client.ts +133 -149
  14. package/core/clientTypes.ts +108 -0
  15. package/core/config.test.ts +22 -11
  16. package/core/config.ts +16 -9
  17. package/core/decrypt/decryptHandleBuilder.ts +51 -45
  18. package/core/decrypt/{tnSealOutput.ts → tnSealOutputV1.ts} +1 -1
  19. package/core/decrypt/tnSealOutputV2.ts +298 -0
  20. package/core/encrypt/cofheMocksZkVerifySign.ts +16 -10
  21. package/core/encrypt/encryptInputsBuilder.test.ts +132 -116
  22. package/core/encrypt/encryptInputsBuilder.ts +159 -111
  23. package/core/encrypt/encryptUtils.ts +6 -3
  24. package/core/encrypt/zkPackProveVerify.ts +70 -8
  25. package/core/error.ts +0 -2
  26. package/core/fetchKeys.test.ts +1 -18
  27. package/core/fetchKeys.ts +0 -26
  28. package/core/index.ts +29 -17
  29. package/core/keyStore.ts +65 -38
  30. package/core/permits.test.ts +253 -1
  31. package/core/permits.ts +80 -16
  32. package/core/types.ts +198 -152
  33. package/core/utils.ts +43 -1
  34. package/dist/adapters.d.cts +38 -20
  35. package/dist/adapters.d.ts +38 -20
  36. package/dist/chains.cjs +14 -1
  37. package/dist/chains.d.cts +23 -1
  38. package/dist/chains.d.ts +23 -1
  39. package/dist/chains.js +1 -1
  40. package/dist/{chunk-LU7BMUUT.js → chunk-UGBVZNRT.js} +39 -25
  41. package/dist/{chunk-GZCQQYVI.js → chunk-WEAZ25JO.js} +14 -2
  42. package/dist/{chunk-KFGPTJ6X.js → chunk-WGCRJCBR.js} +1920 -1692
  43. package/dist/{types-bB7wLj0q.d.cts → clientTypes-5_1nwtUe.d.cts} +308 -347
  44. package/dist/{types-PhwGgQvs.d.ts → clientTypes-Es7fyi65.d.ts} +308 -347
  45. package/dist/core.cjs +2872 -2632
  46. package/dist/core.d.cts +101 -6
  47. package/dist/core.d.ts +101 -6
  48. package/dist/core.js +3 -3
  49. package/dist/node.cjs +2716 -2520
  50. package/dist/node.d.cts +3 -3
  51. package/dist/node.d.ts +3 -3
  52. package/dist/node.js +4 -3
  53. package/dist/{permit-S9CnI6MF.d.cts → permit-fUSe6KKq.d.cts} +31 -15
  54. package/dist/{permit-S9CnI6MF.d.ts → permit-fUSe6KKq.d.ts} +31 -15
  55. package/dist/permits.cjs +39 -24
  56. package/dist/permits.d.cts +137 -148
  57. package/dist/permits.d.ts +137 -148
  58. package/dist/permits.js +1 -1
  59. package/dist/web.cjs +2929 -2518
  60. package/dist/web.d.cts +21 -5
  61. package/dist/web.d.ts +21 -5
  62. package/dist/web.js +185 -9
  63. package/dist/zkProve.worker.cjs +93 -0
  64. package/dist/zkProve.worker.d.cts +2 -0
  65. package/dist/zkProve.worker.d.ts +2 -0
  66. package/dist/zkProve.worker.js +91 -0
  67. package/node/client.test.ts +20 -25
  68. package/node/encryptInputs.test.ts +18 -38
  69. package/node/index.ts +1 -0
  70. package/package.json +14 -14
  71. package/permits/index.ts +1 -0
  72. package/permits/localstorage.test.ts +0 -1
  73. package/permits/permit.test.ts +25 -22
  74. package/permits/permit.ts +30 -21
  75. package/permits/sealing.test.ts +3 -3
  76. package/permits/sealing.ts +2 -2
  77. package/permits/store.ts +5 -7
  78. package/permits/test-utils.ts +1 -1
  79. package/permits/types.ts +17 -0
  80. package/permits/utils.ts +0 -1
  81. package/permits/validation.ts +24 -4
  82. package/web/client.web.test.ts +20 -25
  83. package/web/config.web.test.ts +0 -2
  84. package/web/encryptInputs.web.test.ts +31 -54
  85. package/web/index.ts +65 -1
  86. package/web/storage.ts +19 -5
  87. package/web/worker.builder.web.test.ts +148 -0
  88. package/web/worker.config.web.test.ts +329 -0
  89. package/web/worker.output.web.test.ts +84 -0
  90. package/web/workerManager.test.ts +80 -0
  91. package/web/workerManager.ts +214 -0
  92. package/web/workerManager.web.test.ts +114 -0
  93. package/web/zkProve.worker.ts +133 -0
  94. package/core/result.test.ts +0 -180
  95. package/core/result.ts +0 -67
  96. package/core/test-utils.ts +0 -45
@@ -1,8 +1,13 @@
1
- /* eslint-disable no-unused-vars */
2
-
3
- import { type ZkBuilderAndCrsGenerator, zkPack, zkProve, zkVerify } from './zkPackProveVerify.js';
1
+ import {
2
+ type ZkBuilderAndCrsGenerator,
3
+ type ZkProveWorkerFunction,
4
+ zkPack,
5
+ zkProve,
6
+ zkProveWithWorker,
7
+ zkVerify,
8
+ constructZkPoKMetadata,
9
+ } from './zkPackProveVerify.js';
4
10
  import { CofhesdkError, CofhesdkErrorCode } from '../error.js';
5
- import { type Result, resultWrapper } from '../result.js';
6
11
  import {
7
12
  type EncryptStepCallbackFunction,
8
13
  EncryptStep,
@@ -31,6 +36,7 @@ type EncryptInputsBuilderParams<T extends EncryptableItem[]> = BaseBuilderParams
31
36
  compactPkeCrsDeserializer: FheKeyDeserializer | undefined;
32
37
  zkBuilderAndCrsGenerator: ZkBuilderAndCrsGenerator | undefined;
33
38
  initTfhe: TfheInitializer | undefined;
39
+ zkProveWorkerFn: ZkProveWorkerFunction | undefined;
34
40
 
35
41
  keysStorage: KeysStorage | undefined;
36
42
  };
@@ -39,9 +45,6 @@ type EncryptInputsBuilderParams<T extends EncryptableItem[]> = BaseBuilderParams
39
45
  * EncryptInputsBuilder exposes a builder pattern for encrypting inputs.
40
46
  * account, securityZone, and chainId can be overridden in the builder.
41
47
  * config, tfhePublicKeyDeserializer, compactPkeCrsDeserializer, and zkBuilderAndCrsGenerator are required to be set in the builder.
42
- *
43
- * @dev All errors must be throw in `encrypt`, which wraps them in a Result.
44
- * Do not throw errors in the constructor or in the builder methods.
45
48
  */
46
49
 
47
50
  export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuilder {
@@ -51,13 +54,17 @@ export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuild
51
54
 
52
55
  private zkvWalletClient: WalletClient | undefined;
53
56
 
54
- private tfhePublicKeyDeserializer: FheKeyDeserializer | undefined;
55
- private compactPkeCrsDeserializer: FheKeyDeserializer | undefined;
56
- private zkBuilderAndCrsGenerator: ZkBuilderAndCrsGenerator | undefined;
57
+ private tfhePublicKeyDeserializer: FheKeyDeserializer;
58
+ private compactPkeCrsDeserializer: FheKeyDeserializer;
59
+ private zkBuilderAndCrsGenerator: ZkBuilderAndCrsGenerator;
57
60
  private initTfhe: TfheInitializer | undefined;
61
+ private zkProveWorkerFn: ZkProveWorkerFunction | undefined;
58
62
 
59
63
  private keysStorage: KeysStorage | undefined;
60
64
 
65
+ // Worker configuration (from config, overrideable)
66
+ private useWorker: boolean;
67
+
61
68
  private stepTimestamps: Record<EncryptStep, number> = {
62
69
  [EncryptStep.InitTfhe]: 0,
63
70
  [EncryptStep.FetchKeys]: 0,
@@ -81,12 +88,56 @@ export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuild
81
88
 
82
89
  this.zkvWalletClient = params.zkvWalletClient;
83
90
 
91
+ // Check that tfhePublicKeyDeserializer is provided
92
+ if (!params.tfhePublicKeyDeserializer) {
93
+ throw new CofhesdkError({
94
+ code: CofhesdkErrorCode.MissingTfhePublicKeyDeserializer,
95
+ message: 'EncryptInputsBuilder tfhePublicKeyDeserializer is undefined',
96
+ hint: 'Ensure client has been created with a tfhePublicKeyDeserializer.',
97
+ context: {
98
+ tfhePublicKeyDeserializer: params.tfhePublicKeyDeserializer,
99
+ },
100
+ });
101
+ }
84
102
  this.tfhePublicKeyDeserializer = params.tfhePublicKeyDeserializer;
103
+
104
+ // Check that compactPkeCrsDeserializer is provided
105
+ if (!params.compactPkeCrsDeserializer) {
106
+ throw new CofhesdkError({
107
+ code: CofhesdkErrorCode.MissingCompactPkeCrsDeserializer,
108
+ message: 'EncryptInputsBuilder compactPkeCrsDeserializer is undefined',
109
+ hint: 'Ensure client has been created with a compactPkeCrsDeserializer.',
110
+ context: {
111
+ compactPkeCrsDeserializer: params.compactPkeCrsDeserializer,
112
+ },
113
+ });
114
+ }
85
115
  this.compactPkeCrsDeserializer = params.compactPkeCrsDeserializer;
116
+
117
+ // Check that zkBuilderAndCrsGenerator is provided
118
+ if (!params.zkBuilderAndCrsGenerator) {
119
+ throw new CofhesdkError({
120
+ code: CofhesdkErrorCode.MissingZkBuilderAndCrsGenerator,
121
+ message: 'EncryptInputsBuilder zkBuilderAndCrsGenerator is undefined',
122
+ hint: 'Ensure client has been created with a zkBuilderAndCrsGenerator.',
123
+ context: {
124
+ zkBuilderAndCrsGenerator: params.zkBuilderAndCrsGenerator,
125
+ },
126
+ });
127
+ }
86
128
  this.zkBuilderAndCrsGenerator = params.zkBuilderAndCrsGenerator;
129
+
130
+ // Optional tfhe initialization function, will be run if provided
87
131
  this.initTfhe = params.initTfhe;
88
132
 
133
+ // Optional zkProve worker function, will be used on web if useWorkers is true and worker function is provided
134
+ this.zkProveWorkerFn = params.zkProveWorkerFn;
135
+
136
+ // Keys storage is used to store the FHE key and CRS
89
137
  this.keysStorage = params.keysStorage;
138
+
139
+ // Initialize useWorker from config (can be overridden via setUseWorker) - default to true
140
+ this.useWorker = params.config?.useWorkers ?? true;
90
141
  }
91
142
 
92
143
  /**
@@ -158,6 +209,42 @@ export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuild
158
209
  return this.securityZone;
159
210
  }
160
211
 
212
+ /**
213
+ * @param useWorker - Whether to use Web Workers for ZK proof generation.
214
+ *
215
+ * Overrides the config-level useWorkers setting for this specific encryption.
216
+ *
217
+ * Example:
218
+ * ```typescript
219
+ * const encrypted = await encryptInputs([Encryptable.uint128(10n)])
220
+ * .setUseWorker(false)
221
+ * .encrypt();
222
+ * ```
223
+ *
224
+ * @returns The chainable EncryptInputsBuilder instance.
225
+ */
226
+ setUseWorker(useWorker: boolean): EncryptInputsBuilder<T> {
227
+ this.useWorker = useWorker;
228
+ return this;
229
+ }
230
+
231
+ /**
232
+ * Gets the current worker configuration.
233
+ *
234
+ * @returns Whether Web Workers are enabled for this encryption.
235
+ *
236
+ * Example:
237
+ * ```typescript
238
+ * const builder = encryptInputs([Encryptable.uint128(10n)]);
239
+ * console.log(builder.getUseWorker()); // true (from config)
240
+ * builder.setUseWorker(false);
241
+ * console.log(builder.getUseWorker()); // false (overridden)
242
+ * ```
243
+ */
244
+ getUseWorker(): boolean {
245
+ return this.useWorker;
246
+ }
247
+
161
248
  /**
162
249
  * @param callback - Function to be called with the encryption step.
163
250
  *
@@ -202,50 +289,13 @@ export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuild
202
289
  this.stepCallback(step, { ...context, isStart: false, isEnd: true, duration });
203
290
  }
204
291
 
205
- /**
206
- * tfhePublicKeyDeserializer is a platform-specific dependency injected into core/createCofhesdkClientBase by web/createCofhesdkClient and node/createCofhesdkClient
207
- * web/ uses zama "tfhe"
208
- * node/ uses zama "node-tfhe"
209
- * Users should not set this manually.
210
- */
211
- private getTfhePublicKeyDeserializerOrThrow(): FheKeyDeserializer {
212
- if (this.tfhePublicKeyDeserializer) return this.tfhePublicKeyDeserializer;
213
- throw new CofhesdkError({
214
- code: CofhesdkErrorCode.MissingTfhePublicKeyDeserializer,
215
- message: 'EncryptInputsBuilder tfhePublicKeyDeserializer is undefined',
216
- hint: 'Ensure client has been created with a tfhePublicKeyDeserializer.',
217
- context: {
218
- tfhePublicKeyDeserializer: this.tfhePublicKeyDeserializer,
219
- },
220
- });
221
- }
222
-
223
- /**
224
- * compactPkeCrsDeserializer is a platform-specific dependency injected into core/createCofhesdkClientBase by web/createCofhesdkClient and node/createCofhesdkClient
225
- * web/ uses zama "tfhe"
226
- * node/ uses zama "node-tfhe"
227
- * Users should not set this manually.
228
- */
229
- private getCompactPkeCrsDeserializerOrThrow(): FheKeyDeserializer {
230
- if (this.compactPkeCrsDeserializer) return this.compactPkeCrsDeserializer;
231
- throw new CofhesdkError({
232
- code: CofhesdkErrorCode.MissingCompactPkeCrsDeserializer,
233
- message: 'EncryptInputsBuilder compactPkeCrsDeserializer is undefined',
234
- hint: 'Ensure client has been created with a compactPkeCrsDeserializer.',
235
- context: {
236
- compactPkeCrsDeserializer: this.compactPkeCrsDeserializer,
237
- },
238
- });
239
- }
240
-
241
292
  /**
242
293
  * zkVerifierUrl is included in the chains exported from cofhesdk/chains for use in CofhesdkConfig.supportedChains
243
294
  * Users should generally not set this manually.
244
295
  */
245
296
  private async getZkVerifierUrl(): Promise<string> {
246
- const config = this.getConfigOrThrow();
247
- const chainId = await this.getChainIdOrThrow();
248
- return getZkVerifierUrlOrThrow(config, chainId);
297
+ this.assertChainId();
298
+ return getZkVerifierUrlOrThrow(this.config, this.chainId);
249
299
  }
250
300
 
251
301
  /**
@@ -280,10 +330,7 @@ export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuild
280
330
  crs: string;
281
331
  crsFetchedFromCoFHE: boolean;
282
332
  }> {
283
- const config = this.getConfigOrThrow();
284
- const chainId = await this.getChainIdOrThrow();
285
- const compactPkeCrsDeserializer = this.getCompactPkeCrsDeserializerOrThrow();
286
- const tfhePublicKeyDeserializer = this.getTfhePublicKeyDeserializerOrThrow();
333
+ this.assertChainId();
287
334
  const securityZone = this.getSecurityZone();
288
335
 
289
336
  try {
@@ -305,11 +352,11 @@ export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuild
305
352
 
306
353
  try {
307
354
  [[fheKey, fheKeyFetchedFromCoFHE], [crs, crsFetchedFromCoFHE]] = await fetchKeys(
308
- config,
309
- chainId,
355
+ this.config,
356
+ this.chainId,
310
357
  securityZone,
311
- tfhePublicKeyDeserializer,
312
- compactPkeCrsDeserializer,
358
+ this.tfhePublicKeyDeserializer,
359
+ this.compactPkeCrsDeserializer,
313
360
  this.keysStorage
314
361
  );
315
362
  } catch (error) {
@@ -317,11 +364,11 @@ export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuild
317
364
  code: CofhesdkErrorCode.FetchKeysFailed,
318
365
  message: `Failed to fetch FHE key and CRS`,
319
366
  context: {
320
- config,
321
- chainId,
367
+ config: this.config,
368
+ chainId: this.chainId,
322
369
  securityZone,
323
- compactPkeCrsDeserializer,
324
- tfhePublicKeyDeserializer,
370
+ compactPkeCrsDeserializer: this.compactPkeCrsDeserializer,
371
+ tfhePublicKeyDeserializer: this.tfhePublicKeyDeserializer,
325
372
  },
326
373
  });
327
374
  }
@@ -331,7 +378,7 @@ export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuild
331
378
  code: CofhesdkErrorCode.MissingFheKey,
332
379
  message: `FHE key not found`,
333
380
  context: {
334
- chainId,
381
+ chainId: this.chainId,
335
382
  securityZone,
336
383
  },
337
384
  });
@@ -342,7 +389,7 @@ export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuild
342
389
  code: CofhesdkErrorCode.MissingCrs,
343
390
  message: `CRS not found for chainId <${this.chainId}>`,
344
391
  context: {
345
- chainId,
392
+ chainId: this.chainId,
346
393
  },
347
394
  });
348
395
  }
@@ -350,31 +397,6 @@ export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuild
350
397
  return { fheKey, fheKeyFetchedFromCoFHE, crs, crsFetchedFromCoFHE };
351
398
  }
352
399
 
353
- /**
354
- * zkBuilderAndCrsGenerator is a platform-specific dependency injected into core/createCofhesdkClientBase by web/createCofhesdkClient and node/createCofhesdkClient
355
- * web/ uses zama "tfhe"
356
- * node/ uses zama "node-tfhe"
357
- * Users should not set this manually.
358
- *
359
- * Generates the zkBuilder and zkCrs from the fheKey and crs
360
- */
361
- private generateZkBuilderAndCrs(fheKey: string, crs: string) {
362
- const zkBuilderAndCrsGenerator = this.zkBuilderAndCrsGenerator;
363
-
364
- if (!zkBuilderAndCrsGenerator) {
365
- throw new CofhesdkError({
366
- code: CofhesdkErrorCode.MissingZkBuilderAndCrsGenerator,
367
- message: `zkBuilderAndCrsGenerator is undefined`,
368
- hint: 'Ensure client has been created with a zkBuilderAndCrsGenerator.',
369
- context: {
370
- zkBuilderAndCrsGenerator: this.zkBuilderAndCrsGenerator,
371
- },
372
- });
373
- }
374
-
375
- return zkBuilderAndCrsGenerator(fheKey, crs);
376
- }
377
-
378
400
  /**
379
401
  * @dev Encrypt against the cofheMocks instead of CoFHE
380
402
  *
@@ -382,7 +404,11 @@ export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuild
382
404
  * cofheMocksInsertPackedHashes - stores the ctHashes and their plaintext values for on-chain mocking of FHE operations.
383
405
  * cofheMocksZkCreateProofSignatures - creates signatures to be included in the encrypted inputs. The signers address is known and verified in the mock contracts.
384
406
  */
385
- private async mocksEncrypt(account: string): Promise<[...EncryptedItemInputs<T>]> {
407
+ private async mocksEncrypt(): Promise<[...EncryptedItemInputs<T>]> {
408
+ this.assertAccount();
409
+ this.assertPublicClient();
410
+ this.assertWalletClient();
411
+
386
412
  this.fireStepStart(EncryptStep.InitTfhe);
387
413
  await sleep(100);
388
414
  this.fireStepEnd(EncryptStep.InitTfhe, { tfheInitializationExecuted: false });
@@ -404,10 +430,10 @@ export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuild
404
430
  await sleep(500);
405
431
  const signedResults = await cofheMocksZkVerifySign(
406
432
  this.inputItems,
407
- account,
433
+ this.account,
408
434
  this.securityZone,
409
- this.getPublicClientOrThrow(),
410
- this.getWalletClientOrThrow(),
435
+ this.publicClient,
436
+ this.walletClient,
411
437
  this.zkvWalletClient
412
438
  );
413
439
  const encryptedInputs: EncryptedItemInput[] = signedResults.map(({ ct_hash, signature }, index) => ({
@@ -424,7 +450,10 @@ export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuild
424
450
  /**
425
451
  * In the production context, perform a true encryption with the CoFHE coprocessor.
426
452
  */
427
- private async productionEncrypt(account: string, chainId: number): Promise<[...EncryptedItemInputs<T>]> {
453
+ private async productionEncrypt(): Promise<[...EncryptedItemInputs<T>]> {
454
+ this.assertAccount();
455
+ this.assertChainId();
456
+
428
457
  this.fireStepStart(EncryptStep.InitTfhe);
429
458
 
430
459
  // Deferred initialization of tfhe wasm until encrypt is called
@@ -438,7 +467,8 @@ export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuild
438
467
  // Deferred fetching of fheKey and crs until encrypt is called
439
468
  // if the key/crs is already in the store, it is not fetched from the CoFHE API
440
469
  const { fheKey, fheKeyFetchedFromCoFHE, crs, crsFetchedFromCoFHE } = await this.fetchFheKeyAndCrs();
441
- let { zkBuilder, zkCrs } = this.generateZkBuilderAndCrs(fheKey, crs);
470
+
471
+ let { zkBuilder, zkCrs } = this.zkBuilderAndCrsGenerator(fheKey, crs);
442
472
 
443
473
  this.fireStepEnd(EncryptStep.FetchKeys, { fheKeyFetchedFromCoFHE, crsFetchedFromCoFHE });
444
474
 
@@ -450,15 +480,42 @@ export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuild
450
480
 
451
481
  this.fireStepStart(EncryptStep.Prove);
452
482
 
453
- const proof = await zkProve(zkBuilder, zkCrs, account, this.securityZone, chainId);
483
+ // Construct metadata once (used by both worker and main thread paths)
484
+ const metadata = constructZkPoKMetadata(this.account, this.securityZone, this.chainId);
485
+
486
+ let proof: Uint8Array | null = null;
487
+ let usedWorker = false;
488
+ let workerFailedError: string | undefined;
489
+
490
+ // Decision logic: try worker if enabled and available, fallback to main thread
491
+ if (this.useWorker && this.zkProveWorkerFn) {
492
+ try {
493
+ // Call worker function directly (no packing needed, worker does it)
494
+ proof = await zkProveWithWorker(this.zkProveWorkerFn, fheKey, crs, this.inputItems, metadata);
495
+ usedWorker = true;
496
+ } catch (error) {
497
+ // Worker failed - capture error for debugging
498
+ workerFailedError = error instanceof Error ? error.message : String(error);
499
+ }
500
+ }
454
501
 
455
- this.fireStepEnd(EncryptStep.Prove);
502
+ if (proof == null) {
503
+ // Use main thread directly (workers disabled or unavailable)
504
+ proof = await zkProve(zkBuilder, zkCrs, metadata);
505
+ usedWorker = false;
506
+ }
507
+
508
+ this.fireStepEnd(EncryptStep.Prove, {
509
+ useWorker: this.useWorker,
510
+ usedWorker,
511
+ workerFailedError,
512
+ });
456
513
 
457
514
  this.fireStepStart(EncryptStep.Verify);
458
515
 
459
516
  const zkVerifierUrl = await this.getZkVerifierUrl();
460
517
 
461
- const verifyResults = await zkVerify(zkVerifierUrl, proof, account, this.securityZone, chainId);
518
+ const verifyResults = await zkVerify(zkVerifierUrl, proof, this.account, this.securityZone, this.chainId);
462
519
  // Add securityZone and utype to the verify results
463
520
  const encryptedInputs: EncryptedItemInput[] = verifyResults.map(
464
521
  ({ ct_hash, signature }: { ct_hash: string; signature: string }, index: number) => ({
@@ -493,20 +550,11 @@ export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuild
493
550
  *
494
551
  * @returns The encrypted inputs.
495
552
  */
496
- async encrypt(): Promise<Result<[...EncryptedItemInputs<T>]>> {
497
- return resultWrapper(async () => {
498
- // Ensure cofhe client is connected
499
- await this.requireConnectedOrThrow();
500
-
501
- const account = await this.getAccountOrThrow();
502
- const chainId = await this.getChainIdOrThrow();
553
+ async encrypt(): Promise<[...EncryptedItemInputs<T>]> {
554
+ // On hardhat chain, interact with MockZkVerifier contract instead of CoFHE
555
+ if (this.chainId === hardhat.id) return this.mocksEncrypt();
503
556
 
504
- // On hardhat, interact with MockZkVerifier contract instead of CoFHE
505
- if (chainId === hardhat.id) {
506
- return await this.mocksEncrypt(account);
507
- }
508
-
509
- return await this.productionEncrypt(account, chainId);
510
- });
557
+ // On other chains, interact with CoFHE coprocessor
558
+ return this.productionEncrypt();
511
559
  }
512
560
  }
@@ -1,7 +1,10 @@
1
1
  /* eslint-disable no-redeclare */
2
- /* eslint-disable no-unused-vars */
3
-
4
- import { type EncryptableItem, isEncryptableItem, type EncryptedItemInput, type EncryptedItemInputs } from '../types.js';
2
+ import {
3
+ type EncryptableItem,
4
+ isEncryptableItem,
5
+ type EncryptedItemInput,
6
+ type EncryptedItemInputs,
7
+ } from '../types.js';
5
8
 
6
9
  export function encryptExtract<T>(item: T): EncryptableItem[];
7
10
  export function encryptExtract<T extends any[]>(item: [...T]): EncryptableItem[];
@@ -1,11 +1,45 @@
1
- /* eslint-disable no-unused-vars */
2
-
3
1
  import { CofhesdkError, CofhesdkErrorCode } from '../error.js';
4
2
  import { type EncryptableItem, FheTypes } from '../types.js';
5
3
  import { toBigIntOrThrow, validateBigIntInRange, toHexString, hexToBytes } from '../utils.js';
6
4
 
7
5
  // ===== TYPES =====
8
6
 
7
+ /**
8
+ * Worker function type for ZK proof generation
9
+ * Platform-specific implementations (web) can provide this to enable worker-based proofs
10
+ */
11
+ export type ZkProveWorkerFunction = (
12
+ fheKeyHex: string,
13
+ crsHex: string,
14
+ items: EncryptableItem[],
15
+ metadata: Uint8Array
16
+ ) => Promise<Uint8Array>;
17
+
18
+ /**
19
+ * Message sent from main thread to worker to request proof generation
20
+ */
21
+ export interface ZkProveWorkerRequest {
22
+ id: string;
23
+ type: 'zkProve';
24
+ fheKeyHex: string;
25
+ crsHex: string;
26
+ items: Array<{
27
+ utype: string;
28
+ data: any;
29
+ }>;
30
+ metadata: number[]; // Uint8Array serialized as array
31
+ }
32
+
33
+ /**
34
+ * Message sent from worker back to main thread with proof result
35
+ */
36
+ export interface ZkProveWorkerResponse {
37
+ id: string;
38
+ type: 'success' | 'error' | 'ready';
39
+ result?: number[]; // Uint8Array serialized as array
40
+ error?: string;
41
+ }
42
+
9
43
  export type VerifyResultRaw = {
10
44
  ct_hash: string;
11
45
  signature: string;
@@ -156,15 +190,39 @@ export const zkPack = (items: EncryptableItem[], builder: ZkCiphertextListBuilde
156
190
  return builder;
157
191
  };
158
192
 
193
+ /**
194
+ * Generates ZK proof using Web Worker (offloads heavy WASM computation)
195
+ * Serializes items and calls the platform-specific worker function
196
+ * @param workerFn - Platform-specific worker function (provided by web/index.ts)
197
+ * @param fheKeyHex - Hex-encoded FHE public key for worker deserialization
198
+ * @param crsHex - Hex-encoded CRS for worker deserialization
199
+ * @param items - Encryptable items to pack in the worker
200
+ * @param metadata - Pre-constructed ZK PoK metadata
201
+ * @returns The serialized proven ciphertext list
202
+ */
203
+ export const zkProveWithWorker = async (
204
+ workerFn: ZkProveWorkerFunction,
205
+ fheKeyHex: string,
206
+ crsHex: string,
207
+ items: EncryptableItem[],
208
+ metadata: Uint8Array
209
+ ): Promise<Uint8Array> => {
210
+ return await workerFn(fheKeyHex, crsHex, items, metadata);
211
+ };
212
+
213
+ /**
214
+ * Generates ZK proof using main thread (synchronous WASM)
215
+ * This is the fallback when workers are disabled or unavailable
216
+ * @param builder - The ZK ciphertext list builder with packed inputs
217
+ * @param crs - The Compact PKE CRS for proof generation
218
+ * @param metadata - Pre-constructed ZK PoK metadata
219
+ * @returns The serialized proven ciphertext list
220
+ */
159
221
  export const zkProve = async (
160
222
  builder: ZkCiphertextListBuilder,
161
223
  crs: ZkCompactPkeCrs,
162
- address: string,
163
- securityZone: number,
164
- chainId: number
224
+ metadata: Uint8Array
165
225
  ): Promise<Uint8Array> => {
166
- const metadata = constructZkPoKMetadata(address, securityZone, chainId);
167
-
168
226
  return new Promise((resolve) => {
169
227
  setTimeout(() => {
170
228
  const compactList = builder.build_with_proof_packed(
@@ -178,7 +236,11 @@ export const zkProve = async (
178
236
  });
179
237
  };
180
238
 
181
- const constructZkPoKMetadata = (accountAddr: string, securityZone: number, chainId: number): Uint8Array => {
239
+ /**
240
+ * Constructs the ZK Proof of Knowledge metadata for the proof
241
+ * @internal - Used internally within the encrypt module
242
+ */
243
+ export const constructZkPoKMetadata = (accountAddr: string, securityZone: number, chainId: number): Uint8Array => {
182
244
  // Decode the account address from hex
183
245
  const accountAddrNoPrefix = accountAddr.startsWith('0x') ? accountAddr.slice(2) : accountAddr;
184
246
  const accountBytes = hexToBytes(accountAddrNoPrefix);
package/core/error.ts CHANGED
@@ -1,5 +1,3 @@
1
- /* eslint-disable no-unused-vars */
2
-
3
1
  export enum CofhesdkErrorCode {
4
2
  InternalError = 'INTERNAL_ERROR',
5
3
  UnknownEnvironment = 'UNKNOWN_ENVIRONMENT',
@@ -4,7 +4,7 @@
4
4
  import { sepolia, arbSepolia } from '@/chains';
5
5
 
6
6
  import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
7
- import { fetchKeys, fetchMultichainKeys } from './fetchKeys.js';
7
+ import { fetchKeys } from './fetchKeys.js';
8
8
  import { type CofhesdkConfig, createCofhesdkConfigBase } from './config.js';
9
9
  import { createKeysStore, type KeysStorage } from './keyStore.js';
10
10
 
@@ -192,21 +192,4 @@ describe('fetchKeys', () => {
192
192
  fetchKeys(config, sepolia.id, 0, mockTfhePublicKeyDeserializer, mockCompactPkeCrsDeserializer, keysStorage)
193
193
  ).rejects.toThrow('Error serializing CRS; Error: Serialization failed');
194
194
  });
195
-
196
- it('should fetch and store FHE public key and CRS for all chains in the config', async () => {
197
- await fetchMultichainKeys(config, 0, mockTfhePublicKeyDeserializer, mockCompactPkeCrsDeserializer, keysStorage);
198
-
199
- // Verify keys were stored
200
- const storedFheKey = keysStorage.getFheKey(sepolia.id, 0);
201
- const storedCrs = keysStorage.getCrs(sepolia.id);
202
- const storedFheKeyArb = keysStorage.getFheKey(arbSepolia.id, 0);
203
- const storedCrsArb = keysStorage.getCrs(arbSepolia.id);
204
-
205
- expect(storedFheKey).toBeDefined();
206
- expect(storedCrs).toBeDefined();
207
- expect(storedFheKeyArb).toBeDefined();
208
- expect(storedCrsArb).toBeDefined();
209
- expect(mockTfhePublicKeyDeserializer).toHaveBeenCalled();
210
- expect(mockCompactPkeCrsDeserializer).toHaveBeenCalled();
211
- });
212
195
  });
package/core/fetchKeys.ts CHANGED
@@ -4,7 +4,6 @@ import { type CofhesdkConfig, getCoFheUrlOrThrow } from './config.js';
4
4
  import { type KeysStorage } from './keyStore.js';
5
5
 
6
6
  const PUBLIC_KEY_LENGTH_MIN = 15_000;
7
- // eslint-disable-next-line no-unused-vars
8
7
  export type FheKeyDeserializer = (buff: string) => void;
9
8
 
10
9
  const checkKeyValidity = (key: string | undefined, serializer: FheKeyDeserializer) => {
@@ -143,28 +142,3 @@ export const fetchKeys = async (
143
142
  fetchCrs(coFheUrl, chainId, securityZone, compactPkeCrsDeserializer, keysStorage),
144
143
  ]);
145
144
  };
146
-
147
- /**
148
- * Fetches the FHE public key and the CRS for all chains in the config
149
- * @param {CofhesdkConfig} config - The configuration object for the CoFHE SDK
150
- * @param {number} securityZone - The security zone for which to retrieve the key (default 0).
151
- * @param tfhePublicKeyDeserializer - The serializer for the FHE public key (used for validation).
152
- * @param compactPkeCrsDeserializer - The serializer for the CRS (used for validation).
153
- * @param keysStorage - The keys storage instance to use (optional)
154
- * @returns {Promise<void>} - A promise that resolves when the keys are fetched and stored.
155
- */
156
- export const fetchMultichainKeys = async (
157
- config: CofhesdkConfig,
158
- securityZone: number = 0,
159
- tfhePublicKeyDeserializer: FheKeyDeserializer,
160
- compactPkeCrsDeserializer: FheKeyDeserializer,
161
- keysStorage?: KeysStorage | null
162
- ): Promise<void> => {
163
- await Promise.all(
164
- config.supportedChains
165
- .filter((chain) => chain.id !== hardhat.id)
166
- .map((chain) =>
167
- fetchKeys(config, chain.id, securityZone, tfhePublicKeyDeserializer, compactPkeCrsDeserializer, keysStorage)
168
- )
169
- );
170
- };