@cofhe/sdk 0.1.1 → 0.2.1

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 (107) hide show
  1. package/CHANGELOG.md +22 -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/defineChain.ts +2 -2
  11. package/chains/index.ts +3 -1
  12. package/chains/types.ts +3 -3
  13. package/core/baseBuilder.ts +30 -49
  14. package/core/client.test.ts +200 -72
  15. package/core/client.ts +152 -148
  16. package/core/clientTypes.ts +114 -0
  17. package/core/config.test.ts +30 -11
  18. package/core/config.ts +26 -13
  19. package/core/consts.ts +18 -0
  20. package/core/decrypt/cofheMocksSealOutput.ts +2 -4
  21. package/core/decrypt/decryptHandleBuilder.ts +51 -45
  22. package/core/decrypt/{tnSealOutput.ts → tnSealOutputV1.ts} +1 -1
  23. package/core/decrypt/tnSealOutputV2.ts +298 -0
  24. package/core/encrypt/cofheMocksZkVerifySign.ts +15 -16
  25. package/core/encrypt/encryptInputsBuilder.test.ts +132 -116
  26. package/core/encrypt/encryptInputsBuilder.ts +159 -111
  27. package/core/encrypt/encryptUtils.ts +6 -3
  28. package/core/encrypt/zkPackProveVerify.ts +70 -8
  29. package/core/error.ts +0 -2
  30. package/core/fetchKeys.test.ts +1 -18
  31. package/core/fetchKeys.ts +0 -26
  32. package/core/index.ts +37 -17
  33. package/core/keyStore.ts +65 -38
  34. package/core/permits.test.ts +255 -4
  35. package/core/permits.ts +83 -18
  36. package/core/types.ts +198 -152
  37. package/core/utils.ts +43 -1
  38. package/dist/adapters.d.cts +38 -20
  39. package/dist/adapters.d.ts +38 -20
  40. package/dist/chains.cjs +18 -8
  41. package/dist/chains.d.cts +31 -9
  42. package/dist/chains.d.ts +31 -9
  43. package/dist/chains.js +1 -1
  44. package/dist/{chunk-KFGPTJ6X.js → chunk-I5WFEYXX.js} +1768 -1526
  45. package/dist/{chunk-LU7BMUUT.js → chunk-R3B5TMVX.js} +330 -197
  46. package/dist/{chunk-GZCQQYVI.js → chunk-TBLR7NNE.js} +18 -9
  47. package/dist/{types-PhwGgQvs.d.ts → clientTypes-RqkgkV2i.d.ts} +331 -429
  48. package/dist/{types-bB7wLj0q.d.cts → clientTypes-e4filDzK.d.cts} +331 -429
  49. package/dist/core.cjs +3000 -2625
  50. package/dist/core.d.cts +113 -7
  51. package/dist/core.d.ts +113 -7
  52. package/dist/core.js +3 -3
  53. package/dist/node.cjs +2851 -2526
  54. package/dist/node.d.cts +4 -4
  55. package/dist/node.d.ts +4 -4
  56. package/dist/node.js +4 -3
  57. package/dist/{permit-S9CnI6MF.d.cts → permit-MZ502UBl.d.cts} +54 -41
  58. package/dist/{permit-S9CnI6MF.d.ts → permit-MZ502UBl.d.ts} +54 -41
  59. package/dist/permits.cjs +328 -195
  60. package/dist/permits.d.cts +113 -825
  61. package/dist/permits.d.ts +113 -825
  62. package/dist/permits.js +1 -1
  63. package/dist/types-YiAC4gig.d.cts +33 -0
  64. package/dist/types-YiAC4gig.d.ts +33 -0
  65. package/dist/web.cjs +3067 -2527
  66. package/dist/web.d.cts +22 -6
  67. package/dist/web.d.ts +22 -6
  68. package/dist/web.js +185 -9
  69. package/dist/zkProve.worker.cjs +93 -0
  70. package/dist/zkProve.worker.d.cts +2 -0
  71. package/dist/zkProve.worker.d.ts +2 -0
  72. package/dist/zkProve.worker.js +91 -0
  73. package/node/client.test.ts +20 -25
  74. package/node/encryptInputs.test.ts +18 -38
  75. package/node/index.ts +1 -0
  76. package/package.json +15 -15
  77. package/permits/index.ts +1 -0
  78. package/permits/localstorage.test.ts +9 -14
  79. package/permits/onchain-utils.ts +221 -0
  80. package/permits/permit.test.ts +76 -27
  81. package/permits/permit.ts +58 -95
  82. package/permits/sealing.test.ts +3 -3
  83. package/permits/sealing.ts +2 -2
  84. package/permits/store.test.ts +10 -50
  85. package/permits/store.ts +9 -21
  86. package/permits/test-utils.ts +11 -3
  87. package/permits/types.ts +39 -9
  88. package/permits/utils.ts +0 -5
  89. package/permits/validation.test.ts +29 -32
  90. package/permits/validation.ts +114 -176
  91. package/web/client.web.test.ts +20 -25
  92. package/web/config.web.test.ts +0 -2
  93. package/web/encryptInputs.web.test.ts +31 -54
  94. package/web/index.ts +65 -1
  95. package/web/storage.ts +19 -5
  96. package/web/worker.builder.web.test.ts +148 -0
  97. package/web/worker.config.web.test.ts +329 -0
  98. package/web/worker.output.web.test.ts +84 -0
  99. package/web/workerManager.test.ts +80 -0
  100. package/web/workerManager.ts +214 -0
  101. package/web/workerManager.web.test.ts +114 -0
  102. package/web/zkProve.worker.ts +133 -0
  103. package/core/result.test.ts +0 -180
  104. package/core/result.ts +0 -67
  105. package/core/test-utils.ts +0 -45
  106. package/dist/types-KImPrEIe.d.cts +0 -48
  107. package/dist/types-KImPrEIe.d.ts +0 -48
@@ -0,0 +1,84 @@
1
+ import { arbSepolia as cofhesdkArbSepolia } from '@/chains';
2
+ import { Encryptable } from '@/core';
3
+
4
+ import { describe, it, expect, beforeAll } from 'vitest';
5
+ import type { PublicClient, WalletClient } from 'viem';
6
+ import { createPublicClient, createWalletClient, http } from 'viem';
7
+ import { privateKeyToAccount } from 'viem/accounts';
8
+ import { arbitrumSepolia as viemArbitrumSepolia } from 'viem/chains';
9
+ import { createCofhesdkClient, createCofhesdkConfig } from './index.js';
10
+
11
+ const TEST_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';
12
+
13
+ describe('@cofhe/sdk/web - Worker vs Main Thread Output Validation', () => {
14
+ let publicClient: PublicClient;
15
+ let walletClient: WalletClient;
16
+
17
+ beforeAll(() => {
18
+ publicClient = createPublicClient({
19
+ chain: viemArbitrumSepolia,
20
+ transport: http(),
21
+ });
22
+
23
+ const account = privateKeyToAccount(TEST_PRIVATE_KEY);
24
+ walletClient = createWalletClient({
25
+ chain: viemArbitrumSepolia,
26
+ transport: http(),
27
+ account,
28
+ });
29
+ });
30
+
31
+ it('should produce consistent output format regardless of worker usage', async () => {
32
+ // Create two clients - one with workers, one without
33
+ const configWithWorker = createCofhesdkConfig({
34
+ supportedChains: [cofhesdkArbSepolia],
35
+ useWorkers: true,
36
+ });
37
+
38
+ const configWithoutWorker = createCofhesdkConfig({
39
+ supportedChains: [cofhesdkArbSepolia],
40
+ useWorkers: false,
41
+ });
42
+
43
+ const clientWithWorker = createCofhesdkClient(configWithWorker);
44
+ const clientWithoutWorker = createCofhesdkClient(configWithoutWorker);
45
+
46
+ await clientWithWorker.connect(publicClient, walletClient);
47
+ await clientWithoutWorker.connect(publicClient, walletClient);
48
+
49
+ const value = Encryptable.uint128(12345n);
50
+
51
+ const [resultWithWorker, resultWithoutWorker] = await Promise.all([
52
+ clientWithWorker.encryptInputs([value]).encrypt(),
53
+ clientWithoutWorker.encryptInputs([value]).encrypt(),
54
+ ]);
55
+
56
+ // Both should succeed
57
+ expect(resultWithWorker).toBeDefined();
58
+ expect(resultWithoutWorker).toBeDefined();
59
+
60
+ // Both should have same structure (but different encrypted values)
61
+ const withWorker = resultWithWorker[0];
62
+ const withoutWorker = resultWithoutWorker[0];
63
+
64
+ expect(withWorker).toHaveProperty('ctHash');
65
+ expect(withWorker).toHaveProperty('signature');
66
+ expect(withWorker).toHaveProperty('utype');
67
+ expect(withWorker).toHaveProperty('securityZone');
68
+ expect(withoutWorker).toHaveProperty('ctHash');
69
+ expect(withoutWorker).toHaveProperty('signature');
70
+ expect(withoutWorker).toHaveProperty('utype');
71
+ expect(withoutWorker).toHaveProperty('securityZone');
72
+
73
+ // Format should be identical
74
+ expect(typeof withWorker.ctHash).toBe('bigint');
75
+ expect(typeof withoutWorker.ctHash).toBe('bigint');
76
+ expect(withWorker.signature.startsWith('0x')).toBe(true);
77
+ expect(withoutWorker.signature.startsWith('0x')).toBe(true);
78
+ expect(typeof withWorker.utype).toBe('number');
79
+ expect(typeof withoutWorker.utype).toBe('number');
80
+
81
+ // Note: The actual encrypted values will differ because of randomness
82
+ // in the encryption process, so we don't check equality
83
+ }, 90000);
84
+ });
@@ -0,0 +1,80 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import { getWorkerManager, terminateWorker, areWorkersAvailable } from './workerManager.js';
3
+
4
+ describe('WorkerManager', () => {
5
+ afterEach(() => {
6
+ // Clean up worker after each test
7
+ terminateWorker();
8
+ });
9
+
10
+ describe('areWorkersAvailable', () => {
11
+ it('should return false in Node.js environment', () => {
12
+ // In Node.js/Vitest, workers aren't available
13
+ expect(areWorkersAvailable()).toBe(false);
14
+ });
15
+ });
16
+
17
+ describe('getWorkerManager', () => {
18
+ it('should return a singleton instance', () => {
19
+ const manager1 = getWorkerManager();
20
+ const manager2 = getWorkerManager();
21
+ expect(manager1).toBe(manager2);
22
+ });
23
+
24
+ it('should create new instance after termination', () => {
25
+ const manager1 = getWorkerManager();
26
+ terminateWorker();
27
+ const manager2 = getWorkerManager();
28
+
29
+ // After termination, a new instance should be created
30
+ expect(manager1).toBeDefined();
31
+ expect(manager2).toBeDefined();
32
+ // They should NOT be the same instance anymore
33
+ expect(manager1).not.toBe(manager2);
34
+ });
35
+ });
36
+
37
+ describe('submitProof', () => {
38
+ it('should throw immediately when workers not available', async () => {
39
+ if (!areWorkersAvailable()) {
40
+ const manager = getWorkerManager();
41
+
42
+ await expect(manager.submitProof('invalid', 'invalid', [], new Uint8Array())).rejects.toThrow(
43
+ 'Web Workers not supported'
44
+ );
45
+ }
46
+ });
47
+ });
48
+
49
+ describe('terminateWorker', () => {
50
+ it('should clean up worker instance and allow getting a new one', () => {
51
+ const manager1 = getWorkerManager();
52
+ expect(manager1).toBeDefined();
53
+
54
+ // Terminate the worker
55
+ terminateWorker();
56
+
57
+ // After termination, should be able to get a new instance
58
+ const manager2 = getWorkerManager();
59
+ expect(manager2).toBeDefined();
60
+
61
+ // The new instance should be different from the terminated one
62
+ expect(manager2).not.toBe(manager1);
63
+ });
64
+
65
+ it('should reject pending requests when terminated', async () => {
66
+ if (!areWorkersAvailable()) {
67
+ const manager = getWorkerManager();
68
+
69
+ // Start a request that will fail
70
+ const requestPromise = manager.submitProof('test', 'test', [], new Uint8Array());
71
+
72
+ // Terminate while request is pending
73
+ terminateWorker();
74
+
75
+ // The pending request should be rejected
76
+ await expect(requestPromise).rejects.toThrow();
77
+ }
78
+ });
79
+ });
80
+ });
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Worker Manager for ZK Proof Generation
3
+ * Manages worker lifecycle and request/response handling
4
+ */
5
+
6
+ import type { ZkProveWorkerRequest, ZkProveWorkerResponse } from '@/core';
7
+
8
+ // Declare Worker type for environments where it's not available
9
+ declare const Worker: any;
10
+
11
+ interface PendingRequest {
12
+ resolve: (value: Uint8Array) => void;
13
+ reject: (error: Error) => void;
14
+ timeoutId: ReturnType<typeof setTimeout>;
15
+ }
16
+
17
+ class ZkProveWorkerManager {
18
+ private worker: (typeof Worker extends new (...args: any[]) => infer W ? W : any) | null = null;
19
+ private pendingRequests = new Map<string, PendingRequest>();
20
+ private requestCounter = 0;
21
+ private workerReady = false;
22
+ private initializationPromise: Promise<void> | null = null;
23
+
24
+ /**
25
+ * Initialize the worker
26
+ */
27
+ private async initializeWorker(): Promise<void> {
28
+ if (this.worker && this.workerReady) {
29
+ return;
30
+ }
31
+
32
+ if (this.initializationPromise) {
33
+ return this.initializationPromise;
34
+ }
35
+
36
+ this.initializationPromise = new Promise((resolve, reject) => {
37
+ try {
38
+ // Check if Worker is supported
39
+ if (typeof Worker === 'undefined') {
40
+ reject(new Error('Web Workers not supported'));
41
+ return;
42
+ }
43
+
44
+ // Create worker
45
+ // Note: In production, this will try to load the worker from the same directory
46
+ // The bundler should handle this Worker instantiation
47
+ try {
48
+ this.worker = new Worker(new URL('./zkProve.worker.js', import.meta.url), { type: 'module' }) as any;
49
+ } catch (error) {
50
+ // If Worker creation fails, reject immediately
51
+ reject(new Error(`Failed to create worker: ${error}`));
52
+ return;
53
+ }
54
+
55
+ // Set up message handler
56
+ (this.worker as any).onmessage = (event: any) => {
57
+ const { id, type, result, error } = event.data as ZkProveWorkerResponse;
58
+
59
+ // Handle ready signal
60
+ if (type === 'ready') {
61
+ this.workerReady = true;
62
+ resolve();
63
+ return;
64
+ }
65
+
66
+ // Handle proof responses
67
+ const pending = this.pendingRequests.get(id);
68
+ if (!pending) {
69
+ console.warn('[Worker Manager] Received response for unknown request:', id);
70
+ return;
71
+ }
72
+
73
+ // Clear timeout
74
+ clearTimeout(pending.timeoutId);
75
+ this.pendingRequests.delete(id);
76
+
77
+ if (type === 'success' && result) {
78
+ pending.resolve(new Uint8Array(result));
79
+ } else if (type === 'error') {
80
+ pending.reject(new Error(error || 'Worker error'));
81
+ } else {
82
+ pending.reject(new Error('Invalid response from worker'));
83
+ }
84
+ };
85
+
86
+ // Set up error handler
87
+ (this.worker as any).onerror = (error: any) => {
88
+ console.error('[Worker Manager] Worker error event:', error);
89
+ console.error('[Worker Manager] Error message:', error.message);
90
+ console.error('[Worker Manager] Error filename:', error.filename);
91
+ console.error('[Worker Manager] Error lineno:', error.lineno);
92
+
93
+ // Reject initialization if not ready yet
94
+ if (!this.workerReady) {
95
+ reject(new Error(`Worker failed to initialize: ${error.message || 'Unknown error'}`));
96
+ }
97
+
98
+ // Reject all pending requests
99
+ this.pendingRequests.forEach(({ reject, timeoutId }) => {
100
+ clearTimeout(timeoutId);
101
+ reject(new Error('Worker encountered an error'));
102
+ });
103
+ this.pendingRequests.clear();
104
+ };
105
+
106
+ // Timeout if worker doesn't signal ready within 5 seconds
107
+ setTimeout(() => {
108
+ if (!this.workerReady) {
109
+ reject(new Error('Worker initialization timeout'));
110
+ }
111
+ }, 5000);
112
+ } catch (error) {
113
+ reject(error);
114
+ }
115
+ });
116
+
117
+ return this.initializationPromise;
118
+ }
119
+
120
+ /**
121
+ * Submit a proof generation request to the worker
122
+ */
123
+ async submitProof(
124
+ fheKeyHex: string,
125
+ crsHex: string,
126
+ items: Array<{ utype: string; data: any }>,
127
+ metadata: Uint8Array
128
+ ): Promise<Uint8Array> {
129
+ // Initialize worker if needed
130
+ await this.initializeWorker();
131
+
132
+ // Generate unique request ID
133
+ const id = `zkprove-${Date.now()}-${this.requestCounter++}`;
134
+
135
+ return new Promise((resolve, reject) => {
136
+ // Set up timeout (30 seconds)
137
+ const timeoutId = setTimeout(() => {
138
+ this.pendingRequests.delete(id);
139
+ reject(new Error('Worker request timeout (30s)'));
140
+ }, 30000);
141
+
142
+ // Store pending request
143
+ this.pendingRequests.set(id, { resolve, reject, timeoutId });
144
+
145
+ // Send message to worker
146
+ const message: ZkProveWorkerRequest = {
147
+ id,
148
+ type: 'zkProve',
149
+ fheKeyHex,
150
+ crsHex,
151
+ items,
152
+ metadata: Array.from(metadata),
153
+ };
154
+
155
+ (this.worker as any).postMessage(message);
156
+ });
157
+ }
158
+
159
+ /**
160
+ * Terminate the worker and clean up
161
+ */
162
+ terminate(): void {
163
+ if (this.worker) {
164
+ (this.worker as any).terminate();
165
+ this.worker = null;
166
+ this.workerReady = false;
167
+ this.initializationPromise = null;
168
+ }
169
+
170
+ // Reject all pending requests
171
+ this.pendingRequests.forEach(({ reject, timeoutId }) => {
172
+ clearTimeout(timeoutId);
173
+ reject(new Error('Worker terminated'));
174
+ });
175
+ this.pendingRequests.clear();
176
+ }
177
+
178
+ /**
179
+ * Check if worker is available
180
+ */
181
+ isAvailable(): boolean {
182
+ return typeof Worker !== 'undefined';
183
+ }
184
+ }
185
+
186
+ // Singleton instance
187
+ let workerManager: ZkProveWorkerManager | null = null;
188
+
189
+ /**
190
+ * Get the worker manager instance
191
+ */
192
+ export function getWorkerManager(): ZkProveWorkerManager {
193
+ if (!workerManager) {
194
+ workerManager = new ZkProveWorkerManager();
195
+ }
196
+ return workerManager;
197
+ }
198
+
199
+ /**
200
+ * Terminate the worker
201
+ */
202
+ export function terminateWorker(): void {
203
+ if (workerManager) {
204
+ workerManager.terminate();
205
+ workerManager = null;
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Check if workers are available
211
+ */
212
+ export function areWorkersAvailable(): boolean {
213
+ return typeof Worker !== 'undefined';
214
+ }
@@ -0,0 +1,114 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import { getWorkerManager, terminateWorker, areWorkersAvailable } from './workerManager.js';
3
+
4
+ describe('WorkerManager (Browser)', () => {
5
+ afterEach(() => {
6
+ // Clean up worker after each test
7
+ terminateWorker();
8
+ });
9
+
10
+ describe('areWorkersAvailable', () => {
11
+ it('should return true in browser environment', () => {
12
+ // In real browser with Playwright, workers should be available
13
+ expect(areWorkersAvailable()).toBe(true);
14
+ });
15
+ });
16
+
17
+ describe('Worker lifecycle', () => {
18
+ it('should initialize worker successfully', async () => {
19
+ const manager = getWorkerManager();
20
+ expect(manager).toBeDefined();
21
+
22
+ // Worker should be available
23
+ expect(areWorkersAvailable()).toBe(true);
24
+ });
25
+
26
+ it('should handle worker termination', () => {
27
+ const manager = getWorkerManager();
28
+ expect(manager).toBeDefined();
29
+
30
+ terminateWorker();
31
+
32
+ // Should be able to get a new instance
33
+ const newManager = getWorkerManager();
34
+ expect(newManager).toBeDefined();
35
+ });
36
+ });
37
+
38
+ describe('submitProof', () => {
39
+ it('should reject invalid data with error message', async () => {
40
+ const manager = getWorkerManager();
41
+
42
+ // Submit with invalid data - should fail quickly
43
+ await expect(manager.submitProof('invalid', 'invalid', [], new Uint8Array())).rejects.toThrow();
44
+ }, 35000);
45
+
46
+ it('should reject invalid message type', async () => {
47
+ const manager = getWorkerManager();
48
+
49
+ // Submit with invalid data that will cause worker error
50
+ await expect(
51
+ manager.submitProof(
52
+ '', // empty key
53
+ '', // empty crs
54
+ [{ utype: 'invalid', data: 'test' }],
55
+ new Uint8Array([1, 2, 3])
56
+ )
57
+ ).rejects.toThrow();
58
+ }, 35000);
59
+ });
60
+
61
+ describe('Concurrent requests', () => {
62
+ it('should handle multiple concurrent proof requests', async () => {
63
+ const manager = getWorkerManager();
64
+
65
+ // Submit multiple requests concurrently
66
+ const requests = [
67
+ manager.submitProof('key1', 'crs1', [], new Uint8Array([1])),
68
+ manager.submitProof('key2', 'crs2', [], new Uint8Array([2])),
69
+ manager.submitProof('key3', 'crs3', [], new Uint8Array([3])),
70
+ ];
71
+
72
+ // All should reject (invalid data), but shouldn't crash
73
+ const results = await Promise.allSettled(requests);
74
+
75
+ expect(results).toHaveLength(3);
76
+ results.forEach((result) => {
77
+ expect(result.status).toBe('rejected');
78
+ });
79
+ }, 35000);
80
+ });
81
+
82
+ describe('Worker error handling', () => {
83
+ it('should handle worker errors gracefully', async () => {
84
+ const manager = getWorkerManager();
85
+
86
+ // This should cause an error in the worker
87
+ await expect(
88
+ manager.submitProof(
89
+ 'malformed-hex',
90
+ 'malformed-crs',
91
+ [{ utype: 'uint128', data: 'not-a-number' }],
92
+ new Uint8Array([])
93
+ )
94
+ ).rejects.toThrow();
95
+ }, 35000);
96
+ });
97
+
98
+ describe('Termination during processing', () => {
99
+ it('should reject pending requests on termination', async () => {
100
+ const manager = getWorkerManager();
101
+
102
+ // Start a request (will timeout)
103
+ const proofPromise = manager.submitProof('test', 'test', [], new Uint8Array());
104
+
105
+ // Terminate immediately
106
+ setTimeout(() => {
107
+ terminateWorker();
108
+ }, 100);
109
+
110
+ // Request should be rejected
111
+ await expect(proofPromise).rejects.toThrow();
112
+ }, 5000);
113
+ });
114
+ });
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Web Worker for ZK Proof Generation
3
+ * Performs heavy WASM computation off the main thread
4
+ */
5
+
6
+ /// <reference lib="webworker" />
7
+ /* eslint-disable no-undef */
8
+
9
+ import type { ZkProveWorkerRequest, ZkProveWorkerResponse } from '../core/encrypt/zkPackProveVerify.js';
10
+
11
+ // TFHE module (will be initialized on first use)
12
+ let tfheModule: any = null;
13
+ let initialized = false;
14
+
15
+ /**
16
+ * Initialize TFHE in worker context
17
+ */
18
+ async function initTfhe() {
19
+ if (initialized) return;
20
+
21
+ try {
22
+ // Dynamic import of tfhe module
23
+ tfheModule = await import('tfhe');
24
+ await tfheModule.default();
25
+ await tfheModule.init_panic_hook();
26
+ initialized = true;
27
+ console.log('[Worker] TFHE initialized');
28
+ } catch (error) {
29
+ console.error('[Worker] Failed to initialize TFHE:', error);
30
+ throw error;
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Convert hex string to Uint8Array
36
+ */
37
+ function fromHexString(hexString: string): Uint8Array {
38
+ const cleanString = hexString.length % 2 === 1 ? `0${hexString}` : hexString;
39
+ const arr = cleanString.replace(/^0x/, '').match(/.{1,2}/g);
40
+ if (!arr) return new Uint8Array();
41
+ return new Uint8Array(arr.map((byte) => parseInt(byte, 16)));
42
+ }
43
+
44
+ /**
45
+ * Main message handler
46
+ */
47
+ self.onmessage = async (event: MessageEvent) => {
48
+ const { id, type, fheKeyHex, crsHex, items, metadata } = event.data as ZkProveWorkerRequest;
49
+
50
+ if (type !== 'zkProve') {
51
+ self.postMessage({
52
+ id,
53
+ type: 'error',
54
+ error: 'Invalid message type',
55
+ } as ZkProveWorkerResponse);
56
+ return;
57
+ }
58
+
59
+ try {
60
+ // Initialize TFHE if needed
61
+ await initTfhe();
62
+
63
+ if (!tfheModule) {
64
+ throw new Error('TFHE module not initialized');
65
+ }
66
+
67
+ // Deserialize FHE public key and CRS from hex strings
68
+ const fheKeyBytes = fromHexString(fheKeyHex);
69
+ const crsBytes = fromHexString(crsHex);
70
+
71
+ const fheKey = tfheModule.TfheCompactPublicKey.deserialize(fheKeyBytes);
72
+ const crs = tfheModule.CompactPkeCrs.deserialize(crsBytes);
73
+
74
+ // Create builder
75
+ const builder = tfheModule.ProvenCompactCiphertextList.builder(fheKey);
76
+
77
+ // Pack all items (duplicate of zkPack logic)
78
+ for (const item of items) {
79
+ switch (item.utype) {
80
+ case 'bool':
81
+ builder.push_boolean(Boolean(item.data));
82
+ break;
83
+ case 'uint8':
84
+ builder.push_u8(Number(item.data));
85
+ break;
86
+ case 'uint16':
87
+ builder.push_u16(Number(item.data));
88
+ break;
89
+ case 'uint32':
90
+ builder.push_u32(Number(item.data));
91
+ break;
92
+ case 'uint64':
93
+ builder.push_u64(BigInt(item.data));
94
+ break;
95
+ case 'uint128':
96
+ builder.push_u128(BigInt(item.data));
97
+ break;
98
+ case 'uint160':
99
+ builder.push_u160(BigInt(item.data));
100
+ break;
101
+ default:
102
+ throw new Error(`Unsupported type: ${item.utype}`);
103
+ }
104
+ }
105
+
106
+ // THE HEAVY OPERATION - but in worker thread!
107
+ const metadataBytes = new Uint8Array(metadata);
108
+ const compactList = builder.build_with_proof_packed(crs, metadataBytes, 1);
109
+
110
+ // Serialize result
111
+ const result = compactList.serialize();
112
+
113
+ // Send success response
114
+ self.postMessage({
115
+ id,
116
+ type: 'success',
117
+ result: Array.from(result),
118
+ } as ZkProveWorkerResponse);
119
+ } catch (error) {
120
+ // Send error response
121
+ self.postMessage({
122
+ id,
123
+ type: 'error',
124
+ error: error instanceof Error ? error.message : String(error),
125
+ } as ZkProveWorkerResponse);
126
+ }
127
+ };
128
+
129
+ // Signal ready - send proper message format
130
+ self.postMessage({
131
+ id: 'init',
132
+ type: 'ready',
133
+ } as ZkProveWorkerResponse);