@cofhe/sdk 0.7.1 → 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.
- package/CHANGELOG.md +35 -0
- package/acps/acp.ts +10 -1
- package/acps/index.ts +11 -0
- package/acps/onchain-utils.ts +188 -49
- package/acps/registry.ts +73 -0
- package/acps/test/onchainAccessStatus.test.ts +116 -0
- package/acps/types.ts +16 -0
- package/chains/chains/arbitrum.ts +14 -0
- package/chains/chains/ethereum.ts +14 -0
- package/chains/index.ts +5 -1
- package/chains/test/chains.test.ts +24 -3
- package/core/acps.ts +51 -143
- package/core/client.ts +13 -8
- package/core/clientTypes.ts +29 -4
- package/core/config.ts +25 -1
- package/core/consts.ts +14 -2
- package/core/decrypt/verifyDecryptResult.ts +4 -3
- package/core/encrypt/encryptInputsBuilder.ts +6 -0
- package/core/encrypt/zkPackProveVerify.ts +7 -1
- package/core/error.ts +1 -0
- package/core/fetchKeys.ts +2 -2
- package/core/index.ts +9 -1
- package/core/keyStore.ts +23 -10
- package/core/taskManager.ts +48 -0
- package/core/test/acps.test.ts +91 -1
- package/core/test/client.test.ts +41 -0
- package/core/test/config.test.ts +16 -0
- package/core/test/decrypt.test.ts +11 -4
- package/core/test/encryptInputsBuilder.test.ts +1 -1
- package/core/test/fetchKeys.test.ts +23 -2
- package/core/test/keyStore.test.ts +18 -6
- package/core/test/taskManager.test.ts +57 -0
- package/core/types.ts +8 -0
- package/dist/acps.cjs +314 -35
- package/dist/acps.d.cts +251 -4
- package/dist/acps.d.ts +251 -4
- package/dist/acps.js +2 -2
- package/dist/chains.cjs +26 -0
- package/dist/chains.d.cts +45 -1
- package/dist/chains.d.ts +45 -1
- package/dist/chains.js +1 -1
- package/dist/{chunk-43USWPEH.js → chunk-GGI7Y6AW.js} +59 -264
- package/dist/chunk-KSOV5B4B.js +50 -0
- package/dist/{chunk-ESMZCFJY.js → chunk-NMKMCAI3.js} +4 -2
- package/dist/{chunk-Q7CBWGQX.js → chunk-UJYOBHYA.js} +374 -37
- package/dist/{chunk-N6IDQRRU.js → chunk-UKQAJK4N.js} +25 -1
- package/dist/{clientTypes-BN3nbzYM.d.ts → clientTypes-5YPPR16m.d.ts} +77 -14
- package/dist/{clientTypes-CYZjFznO.d.cts → clientTypes-D3aIXmUp.d.cts} +77 -14
- package/dist/core.cjs +271 -136
- package/dist/core.d.cts +29 -8
- package/dist/core.d.ts +29 -8
- package/dist/core.js +4 -4
- package/dist/node.cjs +266 -136
- package/dist/node.d.cts +2 -2
- package/dist/node.d.ts +2 -2
- package/dist/node.js +5 -5
- package/dist/{acp-Wi6isVQI.d.cts → onchain-utils-CUAK8glx.d.cts} +31 -1
- package/dist/{acp-Wi6isVQI.d.ts → onchain-utils-CUAK8glx.d.ts} +31 -1
- package/dist/web.cjs +360 -150
- package/dist/web.d.cts +49 -3
- package/dist/web.d.ts +49 -3
- package/dist/web.js +51 -20
- package/dist/zkProve.worker.cjs +69 -15
- package/dist/zkProve.worker.js +23 -17
- package/node/storage.ts +2 -2
- package/node/test/inherited.test.ts +5 -1
- package/node/test/storage-memory-fallback.test.ts +47 -0
- package/package.json +5 -3
- package/web/index.ts +100 -26
- package/web/test/countingZkProveWorker.ts +31 -0
- package/web/test/inherited.web.test.ts +5 -1
- package/web/test/mainThreadPool.web.test.ts +174 -0
- package/web/test/threadPoolBenchHelper.ts +140 -0
- package/web/test/threadPoolOff.web.test.ts +17 -0
- package/web/test/threadPoolOn.web.test.ts +80 -0
- package/web/test/workerInitRace.web.test.ts +79 -0
- package/web/tfheThreadPool.ts +140 -0
- package/web/workerManager.ts +4 -2
- package/web/zkProve.worker.ts +42 -17
package/dist/web.d.cts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import { I as IStorage, C as CofheInputConfig, a as CofheConfig, b as CofheClient, E as EncryptableItem } from './clientTypes-
|
|
1
|
+
import { I as IStorage, C as CofheInputConfig, a as CofheConfig, b as CofheClient, E as EncryptableItem } from './clientTypes-D3aIXmUp.cjs';
|
|
2
2
|
import 'viem';
|
|
3
3
|
import './types-C07FK-cL.cjs';
|
|
4
4
|
import 'zod';
|
|
5
|
-
import './
|
|
5
|
+
import './onchain-utils-CUAK8glx.cjs';
|
|
6
6
|
import 'zustand/vanilla';
|
|
7
7
|
|
|
8
8
|
declare function createSsrStorage(): IStorage;
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Worker Manager for ZK Proof Generation
|
|
12
|
+
* Manages worker lifecycle and request/response handling
|
|
13
|
+
*/
|
|
14
|
+
|
|
10
15
|
/**
|
|
11
16
|
* Terminate the worker
|
|
12
17
|
*/
|
|
@@ -16,8 +21,49 @@ declare function terminateWorker(): void;
|
|
|
16
21
|
*/
|
|
17
22
|
declare function areWorkersAvailable(): boolean;
|
|
18
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Rayon thread pool setup for the browser build of tfhe.
|
|
26
|
+
*
|
|
27
|
+
* `tfhe` is built with wasm-bindgen-rayon, so the ZK proof (the
|
|
28
|
+
* `build_with_proof_packed` call, by far the most expensive step of
|
|
29
|
+
* `encryptInputs`) can run across several threads. Those threads are real Web
|
|
30
|
+
* Workers that share the wasm linear memory, and `initThreadPool` hands them
|
|
31
|
+
* that memory over `postMessage`.
|
|
32
|
+
*
|
|
33
|
+
* Sharing memory across threads is only allowed in a cross-origin-isolated
|
|
34
|
+
* context, so the host page must be served with:
|
|
35
|
+
*
|
|
36
|
+
* Cross-Origin-Opener-Policy: same-origin
|
|
37
|
+
* Cross-Origin-Embedder-Policy: require-corp (or `credentialless`)
|
|
38
|
+
*
|
|
39
|
+
* Plenty of apps can't or don't set those headers, and calling
|
|
40
|
+
* `initThreadPool` without them throws ("SharedArrayBuffer transfer requires
|
|
41
|
+
* self.crossOriginIsolated"). So this is strictly an optimisation: we probe for
|
|
42
|
+
* isolation, and when it isn't available we leave tfhe single-threaded rather
|
|
43
|
+
* than failing the encryption. Single-threaded tfhe works in every context.
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
type TfheThreadPoolResult = {
|
|
47
|
+
/** Whether a multi-threaded rayon pool is now running */
|
|
48
|
+
enabled: boolean;
|
|
49
|
+
/** Threads requested from `initThreadPool` (1 when single-threaded) */
|
|
50
|
+
threads: number;
|
|
51
|
+
/** Why the pool was not started, when `enabled` is false */
|
|
52
|
+
reason?: string;
|
|
53
|
+
};
|
|
54
|
+
|
|
19
55
|
declare const hasDOM: boolean;
|
|
20
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Outcome of the rayon thread pool setup on the main thread, or `null` if the
|
|
59
|
+
* main thread hasn't had to generate a proof yet. `enabled: false` carries a
|
|
60
|
+
* `reason`.
|
|
61
|
+
*
|
|
62
|
+
* Note this reflects the *main thread* only. When workers are enabled (the
|
|
63
|
+
* default) proving happens inside the zkProve worker, which runs its own pool,
|
|
64
|
+
* and the main thread never starts one — so `null` is the normal value there.
|
|
65
|
+
*/
|
|
66
|
+
declare function getTfheThreadPoolStatus(): TfheThreadPoolResult | null;
|
|
21
67
|
/**
|
|
22
68
|
* Creates a CoFHE configuration for web with IndexedDB storage as default
|
|
23
69
|
* @param config - The CoFHE input configuration (fheKeyStorage will default to IndexedDB if not provided)
|
|
@@ -39,4 +85,4 @@ declare function createCofheClient<TConfig extends CofheConfig>(config: TConfig)
|
|
|
39
85
|
*/
|
|
40
86
|
declare function createCofheClientWithCustomWorker(config: CofheConfig, customZkProveWorkerFn: (fheKeyHex: string, crsHex: string, items: EncryptableItem[], metadata: Uint8Array) => Promise<Uint8Array>): CofheClient;
|
|
41
87
|
|
|
42
|
-
export { areWorkersAvailable, createCofheClient, createCofheClientWithCustomWorker, createCofheConfig, createSsrStorage, hasDOM, terminateWorker };
|
|
88
|
+
export { areWorkersAvailable, createCofheClient, createCofheClientWithCustomWorker, createCofheConfig, createSsrStorage, getTfheThreadPoolStatus, hasDOM, terminateWorker };
|
package/dist/web.d.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import { I as IStorage, C as CofheInputConfig, a as CofheConfig, b as CofheClient, E as EncryptableItem } from './clientTypes-
|
|
1
|
+
import { I as IStorage, C as CofheInputConfig, a as CofheConfig, b as CofheClient, E as EncryptableItem } from './clientTypes-5YPPR16m.js';
|
|
2
2
|
import 'viem';
|
|
3
3
|
import './types-C07FK-cL.js';
|
|
4
4
|
import 'zod';
|
|
5
|
-
import './
|
|
5
|
+
import './onchain-utils-CUAK8glx.js';
|
|
6
6
|
import 'zustand/vanilla';
|
|
7
7
|
|
|
8
8
|
declare function createSsrStorage(): IStorage;
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Worker Manager for ZK Proof Generation
|
|
12
|
+
* Manages worker lifecycle and request/response handling
|
|
13
|
+
*/
|
|
14
|
+
|
|
10
15
|
/**
|
|
11
16
|
* Terminate the worker
|
|
12
17
|
*/
|
|
@@ -16,8 +21,49 @@ declare function terminateWorker(): void;
|
|
|
16
21
|
*/
|
|
17
22
|
declare function areWorkersAvailable(): boolean;
|
|
18
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Rayon thread pool setup for the browser build of tfhe.
|
|
26
|
+
*
|
|
27
|
+
* `tfhe` is built with wasm-bindgen-rayon, so the ZK proof (the
|
|
28
|
+
* `build_with_proof_packed` call, by far the most expensive step of
|
|
29
|
+
* `encryptInputs`) can run across several threads. Those threads are real Web
|
|
30
|
+
* Workers that share the wasm linear memory, and `initThreadPool` hands them
|
|
31
|
+
* that memory over `postMessage`.
|
|
32
|
+
*
|
|
33
|
+
* Sharing memory across threads is only allowed in a cross-origin-isolated
|
|
34
|
+
* context, so the host page must be served with:
|
|
35
|
+
*
|
|
36
|
+
* Cross-Origin-Opener-Policy: same-origin
|
|
37
|
+
* Cross-Origin-Embedder-Policy: require-corp (or `credentialless`)
|
|
38
|
+
*
|
|
39
|
+
* Plenty of apps can't or don't set those headers, and calling
|
|
40
|
+
* `initThreadPool` without them throws ("SharedArrayBuffer transfer requires
|
|
41
|
+
* self.crossOriginIsolated"). So this is strictly an optimisation: we probe for
|
|
42
|
+
* isolation, and when it isn't available we leave tfhe single-threaded rather
|
|
43
|
+
* than failing the encryption. Single-threaded tfhe works in every context.
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
type TfheThreadPoolResult = {
|
|
47
|
+
/** Whether a multi-threaded rayon pool is now running */
|
|
48
|
+
enabled: boolean;
|
|
49
|
+
/** Threads requested from `initThreadPool` (1 when single-threaded) */
|
|
50
|
+
threads: number;
|
|
51
|
+
/** Why the pool was not started, when `enabled` is false */
|
|
52
|
+
reason?: string;
|
|
53
|
+
};
|
|
54
|
+
|
|
19
55
|
declare const hasDOM: boolean;
|
|
20
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Outcome of the rayon thread pool setup on the main thread, or `null` if the
|
|
59
|
+
* main thread hasn't had to generate a proof yet. `enabled: false` carries a
|
|
60
|
+
* `reason`.
|
|
61
|
+
*
|
|
62
|
+
* Note this reflects the *main thread* only. When workers are enabled (the
|
|
63
|
+
* default) proving happens inside the zkProve worker, which runs its own pool,
|
|
64
|
+
* and the main thread never starts one — so `null` is the normal value there.
|
|
65
|
+
*/
|
|
66
|
+
declare function getTfheThreadPoolStatus(): TfheThreadPoolResult | null;
|
|
21
67
|
/**
|
|
22
68
|
* Creates a CoFHE configuration for web with IndexedDB storage as default
|
|
23
69
|
* @param config - The CoFHE input configuration (fheKeyStorage will default to IndexedDB if not provided)
|
|
@@ -39,4 +85,4 @@ declare function createCofheClient<TConfig extends CofheConfig>(config: TConfig)
|
|
|
39
85
|
*/
|
|
40
86
|
declare function createCofheClientWithCustomWorker(config: CofheConfig, customZkProveWorkerFn: (fheKeyHex: string, crsHex: string, items: EncryptableItem[], metadata: Uint8Array) => Promise<Uint8Array>): CofheClient;
|
|
41
87
|
|
|
42
|
-
export { areWorkersAvailable, createCofheClient, createCofheClientWithCustomWorker, createCofheConfig, createSsrStorage, hasDOM, terminateWorker };
|
|
88
|
+
export { areWorkersAvailable, createCofheClient, createCofheClientWithCustomWorker, createCofheConfig, createSsrStorage, getTfheThreadPoolStatus, hasDOM, terminateWorker };
|
package/dist/web.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { createCofheConfigBase, createCofheClientBase, fheTypeToString } from './chunk-
|
|
2
|
-
import './chunk-
|
|
3
|
-
import './chunk-
|
|
4
|
-
import {
|
|
1
|
+
import { createCofheConfigBase, createCofheClientBase, fheTypeToString } from './chunk-GGI7Y6AW.js';
|
|
2
|
+
import './chunk-UKQAJK4N.js';
|
|
3
|
+
import './chunk-UJYOBHYA.js';
|
|
4
|
+
import { initTfheThreadPool } from './chunk-KSOV5B4B.js';
|
|
5
|
+
import { TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT } from './chunk-NMKMCAI3.js';
|
|
5
6
|
import { constructClient } from 'iframe-shared-storage';
|
|
6
7
|
|
|
7
8
|
// web/const.ts
|
|
@@ -128,7 +129,7 @@ var ZkProveWorkerManager = class {
|
|
|
128
129
|
/**
|
|
129
130
|
* Submit a proof generation request to the worker
|
|
130
131
|
*/
|
|
131
|
-
async submitProof(fheKeyHex, crsHex, items, metadata) {
|
|
132
|
+
async submitProof(fheKeyHex, crsHex, items, metadata, tfheThreads) {
|
|
132
133
|
await this.initializeWorker();
|
|
133
134
|
const id = `zkprove-${Date.now()}-${this.requestCounter++}`;
|
|
134
135
|
return new Promise((resolve, reject) => {
|
|
@@ -143,7 +144,8 @@ var ZkProveWorkerManager = class {
|
|
|
143
144
|
fheKeyHex,
|
|
144
145
|
crsHex,
|
|
145
146
|
items,
|
|
146
|
-
metadata: Array.from(metadata)
|
|
147
|
+
metadata: Array.from(metadata),
|
|
148
|
+
tfheThreads
|
|
147
149
|
};
|
|
148
150
|
this.worker.postMessage(message);
|
|
149
151
|
});
|
|
@@ -191,14 +193,39 @@ function areWorkersAvailable() {
|
|
|
191
193
|
// web/index.ts
|
|
192
194
|
var tfheModule = null;
|
|
193
195
|
var tfheInitialized = false;
|
|
194
|
-
|
|
196
|
+
var tfheInitPromise = null;
|
|
197
|
+
var threadPoolPromise = null;
|
|
198
|
+
var threadPoolResult = null;
|
|
199
|
+
function getTfheThreadPoolStatus() {
|
|
200
|
+
return threadPoolResult;
|
|
201
|
+
}
|
|
202
|
+
var initTfhe = async () => {
|
|
195
203
|
if (tfheInitialized)
|
|
196
204
|
return false;
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
205
|
+
if (tfheInitPromise) {
|
|
206
|
+
await tfheInitPromise;
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
tfheInitPromise = (async () => {
|
|
210
|
+
const mod = await import('tfhe');
|
|
211
|
+
await mod.default();
|
|
212
|
+
await mod.init_panic_hook();
|
|
213
|
+
tfheModule = mod;
|
|
214
|
+
tfheInitialized = true;
|
|
215
|
+
})();
|
|
216
|
+
try {
|
|
217
|
+
await tfheInitPromise;
|
|
218
|
+
} catch (error) {
|
|
219
|
+
tfheInitPromise = null;
|
|
220
|
+
throw error;
|
|
221
|
+
}
|
|
201
222
|
return true;
|
|
223
|
+
};
|
|
224
|
+
function createStartMainThreadPool(tfheThreads) {
|
|
225
|
+
return async () => {
|
|
226
|
+
threadPoolPromise ??= initTfheThreadPool(requireTfhe(), tfheThreads);
|
|
227
|
+
threadPoolResult = await threadPoolPromise;
|
|
228
|
+
};
|
|
202
229
|
}
|
|
203
230
|
function requireTfhe() {
|
|
204
231
|
if (!tfheModule) {
|
|
@@ -234,13 +261,15 @@ var zkBuilderAndCrsGenerator = (fhe, crs) => {
|
|
|
234
261
|
const zkCrs = _deserializeCompactPkeCrs(crs);
|
|
235
262
|
return { zkBuilder, zkCrs };
|
|
236
263
|
};
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
264
|
+
function createZkProveWithWorker(tfheThreads) {
|
|
265
|
+
return async function zkProveWithWorker(fheKeyHex, crsHex, items, metadata) {
|
|
266
|
+
const serializedItems = items.map((item) => ({
|
|
267
|
+
utype: fheTypeToString(item.utype),
|
|
268
|
+
data: typeof item.data === "bigint" ? item.data.toString() : item.data
|
|
269
|
+
}));
|
|
270
|
+
const workerManager2 = getWorkerManager();
|
|
271
|
+
return await workerManager2.submitProof(fheKeyHex, crsHex, serializedItems, metadata, tfheThreads);
|
|
272
|
+
};
|
|
244
273
|
}
|
|
245
274
|
function createCofheConfig(config) {
|
|
246
275
|
return createCofheConfigBase({
|
|
@@ -256,9 +285,10 @@ function createCofheClient(config) {
|
|
|
256
285
|
tfhePublicKeyDeserializer,
|
|
257
286
|
compactPkeCrsDeserializer,
|
|
258
287
|
initTfhe,
|
|
288
|
+
beforeMainThreadProve: createStartMainThreadPool(config.tfheThreads),
|
|
259
289
|
// Always provide the worker function if available - config.useWorkers controls usage
|
|
260
290
|
// areWorkersAvailable will return true if the Worker API is available and false in Node.js
|
|
261
|
-
zkProveWorkerFn: areWorkersAvailable() ?
|
|
291
|
+
zkProveWorkerFn: areWorkersAvailable() ? createZkProveWithWorker(config.tfheThreads) : void 0
|
|
262
292
|
});
|
|
263
293
|
}
|
|
264
294
|
function createCofheClientWithCustomWorker(config, customZkProveWorkerFn) {
|
|
@@ -268,8 +298,9 @@ function createCofheClientWithCustomWorker(config, customZkProveWorkerFn) {
|
|
|
268
298
|
tfhePublicKeyDeserializer,
|
|
269
299
|
compactPkeCrsDeserializer,
|
|
270
300
|
initTfhe,
|
|
301
|
+
beforeMainThreadProve: createStartMainThreadPool(config.tfheThreads),
|
|
271
302
|
zkProveWorkerFn: customZkProveWorkerFn
|
|
272
303
|
});
|
|
273
304
|
}
|
|
274
305
|
|
|
275
|
-
export { areWorkersAvailable, createCofheClient, createCofheClientWithCustomWorker, createCofheConfig, createSsrStorage, hasDOM, terminateWorker };
|
|
306
|
+
export { areWorkersAvailable, createCofheClient, createCofheClientWithCustomWorker, createCofheConfig, createSsrStorage, getTfheThreadPoolStatus, hasDOM, terminateWorker };
|
package/dist/zkProve.worker.cjs
CHANGED
|
@@ -3,23 +3,77 @@
|
|
|
3
3
|
// core/consts.ts
|
|
4
4
|
var TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT = BigInt(1 << 30);
|
|
5
5
|
|
|
6
|
-
// web/
|
|
7
|
-
var
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
// web/tfheThreadPool.ts
|
|
7
|
+
var MAX_AUTO_THREADS = 8;
|
|
8
|
+
function resolveThreadCount(setting = "auto") {
|
|
9
|
+
if (setting === false)
|
|
10
|
+
return 1;
|
|
11
|
+
if (typeof setting === "number") {
|
|
12
|
+
return Number.isFinite(setting) && setting >= 1 ? Math.floor(setting) : 1;
|
|
13
|
+
}
|
|
14
|
+
const cores = globalThis.navigator?.hardwareConcurrency ?? 1;
|
|
15
|
+
if (!Number.isFinite(cores) || cores < 1)
|
|
16
|
+
return 1;
|
|
17
|
+
return Math.min(Math.floor(cores), MAX_AUTO_THREADS);
|
|
18
|
+
}
|
|
19
|
+
function isCrossOriginIsolated() {
|
|
20
|
+
return globalThis.crossOriginIsolated === true;
|
|
21
|
+
}
|
|
22
|
+
async function initTfheThreadPool(mod, setting = "auto") {
|
|
23
|
+
if (setting === false) {
|
|
24
|
+
return { enabled: false, threads: 1, reason: "disabled via config.tfheThreads" };
|
|
25
|
+
}
|
|
26
|
+
if (typeof mod.initThreadPool !== "function") {
|
|
27
|
+
return { enabled: false, threads: 1, reason: "tfhe build has no initThreadPool (single-threaded wasm)" };
|
|
28
|
+
}
|
|
29
|
+
if (typeof Worker === "undefined") {
|
|
30
|
+
return { enabled: false, threads: 1, reason: "Worker is unavailable" };
|
|
31
|
+
}
|
|
32
|
+
if (!isCrossOriginIsolated()) {
|
|
33
|
+
return {
|
|
34
|
+
enabled: false,
|
|
35
|
+
threads: 1,
|
|
36
|
+
reason: "not cross-origin isolated \u2014 serve the page with Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp (or credentialless) to enable multi-threaded proving"
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const threads = resolveThreadCount(setting);
|
|
40
|
+
if (threads <= 1) {
|
|
41
|
+
return { enabled: false, threads: 1, reason: "resolved to a single thread" };
|
|
42
|
+
}
|
|
12
43
|
try {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
await tfheModule.init_panic_hook();
|
|
16
|
-
initialized = true;
|
|
17
|
-
console.log("[Worker] TFHE initialized");
|
|
44
|
+
await mod.initThreadPool(threads);
|
|
45
|
+
return { enabled: true, threads };
|
|
18
46
|
} catch (error) {
|
|
19
|
-
|
|
20
|
-
|
|
47
|
+
return {
|
|
48
|
+
enabled: false,
|
|
49
|
+
threads: 1,
|
|
50
|
+
reason: `initThreadPool failed: ${error instanceof Error ? error.message : String(error)}`
|
|
51
|
+
};
|
|
21
52
|
}
|
|
22
53
|
}
|
|
54
|
+
|
|
55
|
+
// web/zkProve.worker.ts
|
|
56
|
+
var tfheModule = null;
|
|
57
|
+
var initPromise = null;
|
|
58
|
+
var threadPool = null;
|
|
59
|
+
function initTfhe(tfheThreads = "auto") {
|
|
60
|
+
initPromise ??= (async () => {
|
|
61
|
+
try {
|
|
62
|
+
tfheModule = await import('tfhe');
|
|
63
|
+
await tfheModule.default();
|
|
64
|
+
await tfheModule.init_panic_hook();
|
|
65
|
+
threadPool = await initTfheThreadPool(tfheModule, tfheThreads);
|
|
66
|
+
console.log(
|
|
67
|
+
threadPool.enabled ? `[Worker] TFHE initialized (rayon thread pool: ${threadPool.threads} threads)` : `[Worker] TFHE initialized (single-threaded: ${threadPool.reason})`
|
|
68
|
+
);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error("[Worker] Failed to initialize TFHE:", error);
|
|
71
|
+
initPromise = null;
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
})();
|
|
75
|
+
return initPromise;
|
|
76
|
+
}
|
|
23
77
|
function fromHexString(hexString) {
|
|
24
78
|
const cleanString = hexString.length % 2 === 1 ? `0${hexString}` : hexString;
|
|
25
79
|
const arr = cleanString.replace(/^0x/, "").match(/.{1,2}/g);
|
|
@@ -29,7 +83,7 @@ function fromHexString(hexString) {
|
|
|
29
83
|
}
|
|
30
84
|
if (typeof self !== "undefined") {
|
|
31
85
|
self.onmessage = async (event) => {
|
|
32
|
-
const { id, type, fheKeyHex, crsHex, items, metadata } = event.data;
|
|
86
|
+
const { id, type, fheKeyHex, crsHex, items, metadata, tfheThreads } = event.data;
|
|
33
87
|
if (type !== "zkProve") {
|
|
34
88
|
self.postMessage({
|
|
35
89
|
id,
|
|
@@ -39,7 +93,7 @@ if (typeof self !== "undefined") {
|
|
|
39
93
|
return;
|
|
40
94
|
}
|
|
41
95
|
try {
|
|
42
|
-
await initTfhe();
|
|
96
|
+
await initTfhe(tfheThreads);
|
|
43
97
|
if (!tfheModule) {
|
|
44
98
|
throw new Error("TFHE module not initialized");
|
|
45
99
|
}
|
package/dist/zkProve.worker.js
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { initTfheThreadPool } from './chunk-KSOV5B4B.js';
|
|
2
|
+
import { TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT } from './chunk-NMKMCAI3.js';
|
|
2
3
|
|
|
3
4
|
// web/zkProve.worker.ts
|
|
4
5
|
var tfheModule = null;
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
6
|
+
var initPromise = null;
|
|
7
|
+
var threadPool = null;
|
|
8
|
+
function initTfhe(tfheThreads = "auto") {
|
|
9
|
+
initPromise ??= (async () => {
|
|
10
|
+
try {
|
|
11
|
+
tfheModule = await import('tfhe');
|
|
12
|
+
await tfheModule.default();
|
|
13
|
+
await tfheModule.init_panic_hook();
|
|
14
|
+
threadPool = await initTfheThreadPool(tfheModule, tfheThreads);
|
|
15
|
+
console.log(
|
|
16
|
+
threadPool.enabled ? `[Worker] TFHE initialized (rayon thread pool: ${threadPool.threads} threads)` : `[Worker] TFHE initialized (single-threaded: ${threadPool.reason})`
|
|
17
|
+
);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
console.error("[Worker] Failed to initialize TFHE:", error);
|
|
20
|
+
initPromise = null;
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
})();
|
|
24
|
+
return initPromise;
|
|
19
25
|
}
|
|
20
26
|
function fromHexString(hexString) {
|
|
21
27
|
const cleanString = hexString.length % 2 === 1 ? `0${hexString}` : hexString;
|
|
@@ -26,7 +32,7 @@ function fromHexString(hexString) {
|
|
|
26
32
|
}
|
|
27
33
|
if (typeof self !== "undefined") {
|
|
28
34
|
self.onmessage = async (event) => {
|
|
29
|
-
const { id, type, fheKeyHex, crsHex, items, metadata } = event.data;
|
|
35
|
+
const { id, type, fheKeyHex, crsHex, items, metadata, tfheThreads } = event.data;
|
|
30
36
|
if (type !== "zkProve") {
|
|
31
37
|
self.postMessage({
|
|
32
38
|
id,
|
|
@@ -36,7 +42,7 @@ if (typeof self !== "undefined") {
|
|
|
36
42
|
return;
|
|
37
43
|
}
|
|
38
44
|
try {
|
|
39
|
-
await initTfhe();
|
|
45
|
+
await initTfhe(tfheThreads);
|
|
40
46
|
if (!tfheModule) {
|
|
41
47
|
throw new Error("TFHE module not initialized");
|
|
42
48
|
}
|
package/node/storage.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { promises as fs } from 'fs';
|
|
|
6
6
|
import { join } from 'path';
|
|
7
7
|
|
|
8
8
|
// Memory storage fallback
|
|
9
|
-
const memoryStorage: Record<string,
|
|
9
|
+
const memoryStorage: Record<string, unknown> = {};
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Creates a node storage implementation using the filesystem
|
|
@@ -34,7 +34,7 @@ export const createNodeStorage = (): IStorage => {
|
|
|
34
34
|
await fs.writeFile(filePath, JSON.stringify(value));
|
|
35
35
|
} catch (e) {
|
|
36
36
|
console.warn('Node.js filesystem modules not available, falling back to memory storage' + e);
|
|
37
|
-
memoryStorage[name] =
|
|
37
|
+
memoryStorage[name] = value;
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
40
|
removeItem: async (name: string) => {
|
|
@@ -16,6 +16,10 @@ import { privateKeyToAccount } from 'viem/accounts';
|
|
|
16
16
|
import { arbitrumSepolia, baseSepolia, sepolia } from 'viem/chains';
|
|
17
17
|
import { createCofheClient, createCofheConfig } from '../index.js';
|
|
18
18
|
|
|
19
|
+
// Live-chain suite: needs the funded TEST_PRIVATE_KEY (transacts against pre-deployed
|
|
20
|
+
// testnet fixtures). Without it — fork PRs, where secrets are unavailable — the suite
|
|
21
|
+
// skips; CI runs it separately with the key (test-live job).
|
|
22
|
+
const LIVE_TESTS = Boolean(TEST_PRIVATE_KEY);
|
|
19
23
|
const DEFAULT_TEST_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';
|
|
20
24
|
const BOB_PRIVATE_KEY = (TEST_PRIVATE_KEY || DEFAULT_TEST_PRIVATE_KEY) as `0x${string}`;
|
|
21
25
|
const ALICE_PRIVATE_KEY = '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d';
|
|
@@ -32,7 +36,7 @@ const VIEM_CHAINS: Record<number, Chain> = {
|
|
|
32
36
|
const testViemChain = STAGING_TESTS ? stagingViemChain : arbitrumSepolia;
|
|
33
37
|
const testCofheChain = STAGING_TESTS ? stagingCofhe : cofheArbSepolia;
|
|
34
38
|
|
|
35
|
-
describe('@cofhe/node - Inherited Client Tests', () => {
|
|
39
|
+
describe.skipIf(!LIVE_TESTS)('@cofhe/node - Inherited Client Tests', () => {
|
|
36
40
|
let cofheClient: CofheClient;
|
|
37
41
|
let publicClient: PublicClient;
|
|
38
42
|
let bobWalletClient: WalletClient;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { tmpdir } from 'os';
|
|
5
|
+
|
|
6
|
+
import { createNodeStorage } from '../storage.js';
|
|
7
|
+
|
|
8
|
+
describe('createNodeStorage memory fallback', () => {
|
|
9
|
+
let originalHome: string | undefined;
|
|
10
|
+
let testDir: string;
|
|
11
|
+
let invalidHome: string;
|
|
12
|
+
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
originalHome = process.env.HOME;
|
|
15
|
+
testDir = await fs.mkdtemp(join(tmpdir(), 'cofhesdk-storage-fallback-'));
|
|
16
|
+
|
|
17
|
+
// HOME points to a regular file, so creating HOME/.cofhesdk fails.
|
|
18
|
+
invalidHome = join(testDir, 'not-a-directory');
|
|
19
|
+
await fs.writeFile(invalidHome, 'file', 'utf8');
|
|
20
|
+
process.env.HOME = invalidHome;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterEach(async () => {
|
|
24
|
+
if (originalHome === undefined) {
|
|
25
|
+
delete process.env.HOME;
|
|
26
|
+
} else {
|
|
27
|
+
process.env.HOME = originalHome;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
await fs.rm(testDir, { recursive: true, force: true });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('round-trips the same value when filesystem storage is unavailable', async () => {
|
|
34
|
+
const storage = createNodeStorage();
|
|
35
|
+
const value = {
|
|
36
|
+
state: {
|
|
37
|
+
fhe: { 1: { 0: 'key' } },
|
|
38
|
+
crs: { 1: 'crs' },
|
|
39
|
+
},
|
|
40
|
+
version: 0,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
await storage.setItem('fallback-roundtrip', value);
|
|
44
|
+
|
|
45
|
+
await expect(storage.getItem('fallback-roundtrip')).resolves.toEqual(value);
|
|
46
|
+
});
|
|
47
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cofhe/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "SDK for Fhenix COFHE coprocessor interaction",
|
|
6
6
|
"main": "./dist/core.cjs",
|
|
@@ -101,9 +101,9 @@
|
|
|
101
101
|
"tsup": "8.0.2",
|
|
102
102
|
"typescript": "5.5.4",
|
|
103
103
|
"vitest": "3.2.4",
|
|
104
|
-
"@cofhe/eslint-config": "0.2.1",
|
|
105
104
|
"@cofhe/test-setup": "0.0.0",
|
|
106
|
-
"@cofhe/tsconfig": "0.1.2"
|
|
105
|
+
"@cofhe/tsconfig": "0.1.2",
|
|
106
|
+
"@cofhe/eslint-config": "0.2.1"
|
|
107
107
|
},
|
|
108
108
|
"publishConfig": {
|
|
109
109
|
"access": "public",
|
|
@@ -116,6 +116,8 @@
|
|
|
116
116
|
"check:types": "tsc --noEmit",
|
|
117
117
|
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
|
|
118
118
|
"test": "vitest run",
|
|
119
|
+
"test:live": "vitest run core/test/decrypt.test.ts node/test/inherited.test.ts web/test/inherited.web.test.ts",
|
|
120
|
+
"test:threadpool": "vitest run web/test/threadPoolOff.web.test.ts web/test/threadPoolOn.web.test.ts web/test/mainThreadPool.web.test.ts web/test/workerInitRace.web.test.ts --no-file-parallelism",
|
|
119
121
|
"test:watch": "vitest",
|
|
120
122
|
"test:coverage": "vitest run --coverage"
|
|
121
123
|
}
|