@cofhe/sdk 0.3.2 → 0.5.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 +38 -0
- package/adapters/{ethers5.test.ts → test/ethers5.test.ts} +2 -2
- package/adapters/{ethers6.test.ts → test/ethers6.test.ts} +2 -2
- package/adapters/{hardhat.hh2.test.ts → test/hardhat.hh2.test.ts} +2 -2
- package/adapters/{index.test.ts → test/index.test.ts} +1 -1
- package/adapters/{wagmi.test.ts → test/wagmi.test.ts} +1 -1
- package/chains/{chains.test.ts → test/chains.test.ts} +1 -1
- package/core/client.ts +15 -5
- package/core/clientTypes.ts +7 -5
- package/core/consts.ts +9 -0
- package/core/decrypt/cofheMocksDecryptForTx.ts +14 -3
- package/core/decrypt/decryptForTxBuilder.ts +24 -10
- package/core/decrypt/decryptForViewBuilder.ts +14 -7
- package/core/decrypt/polling.ts +14 -0
- package/core/decrypt/tnDecryptUtils.ts +65 -0
- package/core/decrypt/{tnDecrypt.ts → tnDecryptV1.ts} +7 -70
- package/core/decrypt/tnDecryptV2.ts +483 -0
- package/core/decrypt/tnSealOutputV2.ts +245 -104
- package/core/decrypt/verifyDecryptResult.ts +65 -0
- package/core/encrypt/cofheMocksZkVerifySign.ts +6 -6
- package/core/encrypt/zkPackProveVerify.ts +10 -19
- package/core/fetchKeys.ts +0 -2
- package/core/index.ts +9 -1
- package/core/keyStore.ts +5 -2
- package/core/permits.ts +8 -3
- package/core/{client.test.ts → test/client.test.ts} +7 -7
- package/core/{config.test.ts → test/config.test.ts} +1 -1
- package/core/test/decrypt.test.ts +252 -0
- package/core/test/decryptBuilders.test.ts +390 -0
- package/core/{encrypt → test}/encryptInputsBuilder.test.ts +61 -6
- package/core/{fetchKeys.test.ts → test/fetchKeys.test.ts} +3 -3
- package/core/{keyStore.test.ts → test/keyStore.test.ts} +5 -3
- package/core/{permits.test.ts → test/permits.test.ts} +42 -1
- package/core/test/pollCallbacks.test.ts +563 -0
- package/core/types.ts +21 -0
- package/dist/chains.d.cts +2 -2
- package/dist/chains.d.ts +2 -2
- package/dist/chunk-4FP4V35O.js +13 -0
- package/dist/{chunk-NWDKXBIP.js → chunk-MRCKUMOS.js} +62 -22
- package/dist/{chunk-LWMRB6SD.js → chunk-S7OKGLFD.js} +615 -198
- package/dist/{clientTypes-Y43CKbOz.d.cts → clientTypes-BSbwairE.d.cts} +38 -13
- package/dist/{clientTypes-PQha8zes.d.ts → clientTypes-DDmcgZ0a.d.ts} +38 -13
- package/dist/core.cjs +691 -235
- package/dist/core.d.cts +24 -6
- package/dist/core.d.ts +24 -6
- package/dist/core.js +3 -2
- package/dist/node.cjs +696 -237
- package/dist/node.d.cts +3 -3
- package/dist/node.d.ts +3 -3
- package/dist/node.js +14 -7
- package/dist/{permit-MZ502UBl.d.ts → permit-DnVMDT5h.d.cts} +34 -4
- package/dist/{permit-MZ502UBl.d.cts → permit-DnVMDT5h.d.ts} +34 -4
- package/dist/permits.cjs +66 -29
- package/dist/permits.d.cts +18 -13
- package/dist/permits.d.ts +18 -13
- package/dist/permits.js +2 -1
- package/dist/web.cjs +718 -242
- package/dist/web.d.cts +8 -4
- package/dist/web.d.ts +8 -4
- package/dist/web.js +34 -11
- package/dist/zkProve.worker.cjs +6 -3
- package/dist/zkProve.worker.js +5 -3
- package/node/index.ts +13 -4
- package/node/test/client.test.ts +25 -0
- package/node/test/config.test.ts +16 -0
- package/node/test/inherited.test.ts +244 -0
- package/node/test/tfheinit.test.ts +56 -0
- package/package.json +24 -22
- package/permits/permit.ts +31 -5
- package/permits/sealing.ts +1 -1
- package/permits/{localstorage.test.ts → test/localstorage.test.ts} +2 -2
- package/permits/{permit.test.ts → test/permit.test.ts} +35 -1
- package/permits/{sealing.test.ts → test/sealing.test.ts} +1 -1
- package/permits/{store.test.ts → test/store.test.ts} +2 -2
- package/permits/{validation.test.ts → test/validation.test.ts} +82 -6
- package/permits/types.ts +1 -1
- package/permits/validation.ts +42 -2
- package/web/const.ts +2 -0
- package/web/index.ts +20 -6
- package/web/storage.ts +18 -3
- package/web/{client.web.test.ts → test/client.web.test.ts} +13 -1
- package/web/test/config.web.test.ts +16 -0
- package/web/test/inherited.web.test.ts +245 -0
- package/web/test/tfheinit.web.test.ts +62 -0
- package/web/{worker.config.web.test.ts → test/worker.config.web.test.ts} +1 -1
- package/web/{worker.output.web.test.ts → test/worker.output.web.test.ts} +1 -1
- package/web/{workerManager.test.ts → test/workerManager.test.ts} +1 -1
- package/web/{workerManager.web.test.ts → test/workerManager.web.test.ts} +1 -1
- package/web/zkProve.worker.ts +4 -3
- package/node/client.test.ts +0 -147
- package/node/config.test.ts +0 -68
- package/node/encryptInputs.test.ts +0 -155
- package/web/config.web.test.ts +0 -69
- package/web/encryptInputs.web.test.ts +0 -172
- package/web/worker.builder.web.test.ts +0 -148
- /package/dist/{types-YiAC4gig.d.cts → types-C07FK-cL.d.cts} +0 -0
- /package/dist/{types-YiAC4gig.d.ts → types-C07FK-cL.d.ts} +0 -0
package/permits/permit.ts
CHANGED
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
validateImportPermit,
|
|
23
23
|
ValidationUtils,
|
|
24
24
|
} from './validation.js';
|
|
25
|
-
import * as z from 'zod';
|
|
26
25
|
import { SignatureUtils } from './signature.js';
|
|
27
26
|
import { GenerateSealingKey, SealingKey } from './sealing.js';
|
|
28
27
|
import { checkPermitValidityOnChain, getAclEIP712Domain } from './onchain-utils.js';
|
|
@@ -217,9 +216,9 @@ export const PermitUtils = {
|
|
|
217
216
|
},
|
|
218
217
|
|
|
219
218
|
/**
|
|
220
|
-
* Validate a permit
|
|
219
|
+
* Validate a permit (schema-level validation)
|
|
221
220
|
*/
|
|
222
|
-
|
|
221
|
+
validateSchema: (permit: Permit) => {
|
|
223
222
|
if (permit.type === 'self') {
|
|
224
223
|
return validateSelfPermit(permit);
|
|
225
224
|
} else if (permit.type === 'sharing') {
|
|
@@ -231,12 +230,28 @@ export const PermitUtils = {
|
|
|
231
230
|
}
|
|
232
231
|
},
|
|
233
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Validate a permit (holistic validation).
|
|
235
|
+
*
|
|
236
|
+
* This validates:
|
|
237
|
+
* - Permit schema (shape + invariants)
|
|
238
|
+
* - Permit is signed
|
|
239
|
+
* - Permit is not expired
|
|
240
|
+
*
|
|
241
|
+
* For schema-only validation, use `validateSchema(permit)`.
|
|
242
|
+
*/
|
|
243
|
+
validate: (permit: Permit) => {
|
|
244
|
+
const validated = PermitUtils.validateSchema(permit);
|
|
245
|
+
ValidationUtils.assertSignedAndNotExpired(validated as Permit);
|
|
246
|
+
return validated;
|
|
247
|
+
},
|
|
248
|
+
|
|
234
249
|
/**
|
|
235
250
|
* Get the permission object from a permit (for use in contracts)
|
|
236
251
|
*/
|
|
237
252
|
getPermission: (permit: Permit, skipValidation = false): Permission => {
|
|
238
253
|
if (!skipValidation) {
|
|
239
|
-
PermitUtils.
|
|
254
|
+
PermitUtils.validateSchema(permit);
|
|
240
255
|
}
|
|
241
256
|
|
|
242
257
|
return {
|
|
@@ -308,8 +323,19 @@ export const PermitUtils = {
|
|
|
308
323
|
},
|
|
309
324
|
|
|
310
325
|
/**
|
|
311
|
-
* Check if permit is
|
|
326
|
+
* Check if permit is signed and not expired
|
|
312
327
|
*/
|
|
328
|
+
isSignedAndNotExpired: (permit: Permit) => {
|
|
329
|
+
return ValidationUtils.isSignedAndNotExpired(permit);
|
|
330
|
+
},
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Assert that permit is signed and not expired
|
|
334
|
+
*/
|
|
335
|
+
assertSignedAndNotExpired: (permit: Permit): void => {
|
|
336
|
+
return ValidationUtils.assertSignedAndNotExpired(permit);
|
|
337
|
+
},
|
|
338
|
+
|
|
313
339
|
isValid: (permit: Permit) => {
|
|
314
340
|
return ValidationUtils.isValid(permit);
|
|
315
341
|
},
|
package/permits/sealing.ts
CHANGED
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
setActivePermitHash,
|
|
12
12
|
PermitUtils,
|
|
13
13
|
permitStore,
|
|
14
|
-
} from '
|
|
15
|
-
import { createMockPermit } from '
|
|
14
|
+
} from '../index.js';
|
|
15
|
+
import { createMockPermit } from '../test-utils.js';
|
|
16
16
|
|
|
17
17
|
// Type declarations for happy-dom environment
|
|
18
18
|
declare const localStorage: {
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
type CreateSelfPermitOptions,
|
|
5
5
|
type CreateSharingPermitOptions,
|
|
6
6
|
type ImportSharedPermitOptions,
|
|
7
|
-
} from '
|
|
7
|
+
} from '../index.js';
|
|
8
8
|
import { createPublicClient, createWalletClient, http, type PublicClient, type WalletClient } from 'viem';
|
|
9
9
|
import { arbitrumSepolia } from 'viem/chains';
|
|
10
10
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
@@ -399,6 +399,29 @@ describe('PermitUtils Tests', () => {
|
|
|
399
399
|
expect(parsed).not.toHaveProperty('sealingPair');
|
|
400
400
|
expect(parsed).not.toHaveProperty('issuerSignature');
|
|
401
401
|
});
|
|
402
|
+
|
|
403
|
+
it('should export sharing permit data with recipient and issuerSignature', async () => {
|
|
404
|
+
const permit = await PermitUtils.createSharingAndSign(
|
|
405
|
+
{
|
|
406
|
+
issuer: bobAddress,
|
|
407
|
+
recipient: aliceAddress,
|
|
408
|
+
name: 'Test Sharing Permit',
|
|
409
|
+
},
|
|
410
|
+
publicClient,
|
|
411
|
+
bobWalletClient
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
const exported = PermitUtils.export(permit);
|
|
415
|
+
const parsed = JSON.parse(exported);
|
|
416
|
+
|
|
417
|
+
expect(parsed.name).toBe('Test Sharing Permit');
|
|
418
|
+
expect(parsed.type).toBe('sharing');
|
|
419
|
+
expect(parsed.issuer).toBe(bobAddress);
|
|
420
|
+
expect(parsed.recipient).toBe(aliceAddress);
|
|
421
|
+
expect(parsed.issuerSignature).toBeDefined();
|
|
422
|
+
expect(parsed.issuerSignature).not.toBe('0x');
|
|
423
|
+
expect(parsed).not.toHaveProperty('sealingPair');
|
|
424
|
+
});
|
|
402
425
|
});
|
|
403
426
|
|
|
404
427
|
describe('updateName', () => {
|
|
@@ -459,6 +482,17 @@ describe('PermitUtils Tests', () => {
|
|
|
459
482
|
expect(validation.valid).toBe(true);
|
|
460
483
|
expect(validation.error).toBeNull();
|
|
461
484
|
});
|
|
485
|
+
|
|
486
|
+
it('should throw on validate() for expired signed permit', async () => {
|
|
487
|
+
const expiredPermit = PermitUtils.createSelf({
|
|
488
|
+
issuer: bobAddress,
|
|
489
|
+
name: 'Expired Permit',
|
|
490
|
+
expiration: Math.floor(Date.now() / 1000) - 3600,
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
const signedExpiredPermit = await PermitUtils.sign(expiredPermit, publicClient, bobWalletClient);
|
|
494
|
+
expect(() => PermitUtils.validate(signedExpiredPermit)).toThrow('Permit is expired');
|
|
495
|
+
});
|
|
462
496
|
});
|
|
463
497
|
|
|
464
498
|
describe('real contract interactions', () => {
|
|
@@ -13,9 +13,9 @@ import {
|
|
|
13
13
|
getActivePermitHash,
|
|
14
14
|
setActivePermitHash,
|
|
15
15
|
PermitUtils,
|
|
16
|
-
} from '
|
|
16
|
+
} from '../index.js';
|
|
17
17
|
|
|
18
|
-
import { createMockPermit } from '
|
|
18
|
+
import { createMockPermit } from '../test-utils.js';
|
|
19
19
|
|
|
20
20
|
describe('Storage Tests', () => {
|
|
21
21
|
const chainId = 1;
|
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
type CreateSelfPermitOptions,
|
|
12
12
|
type CreateSharingPermitOptions,
|
|
13
13
|
type ImportSharedPermitOptions,
|
|
14
|
-
} from '
|
|
15
|
-
import { createMockPermit } from '
|
|
14
|
+
} from '../index.js';
|
|
15
|
+
import { createMockPermit } from '../test-utils.js';
|
|
16
16
|
|
|
17
17
|
describe('Validation Tests', () => {
|
|
18
18
|
describe('validateSelfPermitOptions', () => {
|
|
@@ -247,14 +247,14 @@ describe('Validation Tests', () => {
|
|
|
247
247
|
});
|
|
248
248
|
});
|
|
249
249
|
|
|
250
|
-
describe('
|
|
250
|
+
describe('isSignedAndNotExpired', () => {
|
|
251
251
|
it('should return valid for valid permit', async () => {
|
|
252
252
|
const permit = {
|
|
253
253
|
...(await createMockPermit()),
|
|
254
254
|
expiration: Math.floor(Date.now() / 1000) + 3600,
|
|
255
255
|
issuerSignature: '0x1234567890abcdef' as `0x${string}`,
|
|
256
256
|
};
|
|
257
|
-
const result = ValidationUtils.
|
|
257
|
+
const result = ValidationUtils.isSignedAndNotExpired(permit);
|
|
258
258
|
expect(result.valid).toBe(true);
|
|
259
259
|
expect(result.error).toBeNull();
|
|
260
260
|
});
|
|
@@ -265,7 +265,7 @@ describe('Validation Tests', () => {
|
|
|
265
265
|
expiration: Math.floor(Date.now() / 1000) - 3600,
|
|
266
266
|
issuerSignature: '0x1234567890abcdef' as `0x${string}`,
|
|
267
267
|
};
|
|
268
|
-
const result = ValidationUtils.
|
|
268
|
+
const result = ValidationUtils.isSignedAndNotExpired(permit);
|
|
269
269
|
expect(result.valid).toBe(false);
|
|
270
270
|
expect(result.error).toBe('expired');
|
|
271
271
|
});
|
|
@@ -276,10 +276,86 @@ describe('Validation Tests', () => {
|
|
|
276
276
|
expiration: Math.floor(Date.now() / 1000) + 3600,
|
|
277
277
|
issuerSignature: '0x' as `0x${string}`,
|
|
278
278
|
};
|
|
279
|
-
const result = ValidationUtils.
|
|
279
|
+
const result = ValidationUtils.isSignedAndNotExpired(permit);
|
|
280
280
|
expect(result.valid).toBe(false);
|
|
281
281
|
expect(result.error).toBe('not-signed');
|
|
282
282
|
});
|
|
283
283
|
});
|
|
284
|
+
|
|
285
|
+
describe('assertSignedAndNotExpired', () => {
|
|
286
|
+
it('should not throw for valid permit', async () => {
|
|
287
|
+
const permit = {
|
|
288
|
+
...(await createMockPermit()),
|
|
289
|
+
expiration: Math.floor(Date.now() / 1000) + 3600,
|
|
290
|
+
issuerSignature: '0x1234567890abcdef' as `0x${string}`,
|
|
291
|
+
};
|
|
292
|
+
expect(() => ValidationUtils.assertSignedAndNotExpired(permit)).not.toThrow();
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('should throw for expired permit', async () => {
|
|
296
|
+
const permit = {
|
|
297
|
+
...(await createMockPermit()),
|
|
298
|
+
expiration: Math.floor(Date.now() / 1000) - 3600,
|
|
299
|
+
issuerSignature: '0x1234567890abcdef' as `0x${string}`,
|
|
300
|
+
};
|
|
301
|
+
expect(() => ValidationUtils.assertSignedAndNotExpired(permit)).toThrow('Permit is expired');
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('should throw for unsigned permit', async () => {
|
|
305
|
+
const permit = {
|
|
306
|
+
...(await createMockPermit()),
|
|
307
|
+
expiration: Math.floor(Date.now() / 1000) + 3600,
|
|
308
|
+
issuerSignature: '0x' as `0x${string}`,
|
|
309
|
+
};
|
|
310
|
+
expect(() => ValidationUtils.assertSignedAndNotExpired(permit)).toThrow('Permit is not signed');
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
describe('isValid', () => {
|
|
315
|
+
it('should return invalid-schema for schema-invalid permit', async () => {
|
|
316
|
+
const permit = {
|
|
317
|
+
...(await createMockPermit()),
|
|
318
|
+
type: 'self' as const,
|
|
319
|
+
expiration: Math.floor(Date.now() / 1000) + 3600,
|
|
320
|
+
issuerSignature: '0x1234567890abcdef' as `0x${string}`,
|
|
321
|
+
// Self permits must have recipient == zeroAddress per schema.
|
|
322
|
+
recipient: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8' as `0x${string}`,
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const result = ValidationUtils.isValid(permit as unknown as Permit);
|
|
326
|
+
expect(result.valid).toBe(false);
|
|
327
|
+
expect(result.error).toBe('invalid-schema');
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('should return expired for expired but otherwise schema-valid permit', async () => {
|
|
331
|
+
const permit = {
|
|
332
|
+
...(await createMockPermit()),
|
|
333
|
+
type: 'self' as const,
|
|
334
|
+
expiration: Math.floor(Date.now() / 1000) - 3600,
|
|
335
|
+
issuerSignature: '0x1234567890abcdef' as `0x${string}`,
|
|
336
|
+
recipient: '0x0000000000000000000000000000000000000000' as `0x${string}`,
|
|
337
|
+
recipientSignature: '0x' as `0x${string}`,
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
const result = ValidationUtils.isValid(permit as unknown as Permit);
|
|
341
|
+
expect(result.valid).toBe(false);
|
|
342
|
+
expect(result.error).toBe('expired');
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it('should return valid for schema-valid, signed, non-expired permit', async () => {
|
|
346
|
+
const permit = {
|
|
347
|
+
...(await createMockPermit()),
|
|
348
|
+
type: 'self' as const,
|
|
349
|
+
expiration: Math.floor(Date.now() / 1000) + 3600,
|
|
350
|
+
issuerSignature: '0x1234567890abcdef' as `0x${string}`,
|
|
351
|
+
recipient: '0x0000000000000000000000000000000000000000' as `0x${string}`,
|
|
352
|
+
recipientSignature: '0x' as `0x${string}`,
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const result = ValidationUtils.isValid(permit as unknown as Permit);
|
|
356
|
+
expect(result.valid).toBe(true);
|
|
357
|
+
expect(result.error).toBeNull();
|
|
358
|
+
});
|
|
359
|
+
});
|
|
284
360
|
});
|
|
285
361
|
});
|
package/permits/types.ts
CHANGED
package/permits/validation.ts
CHANGED
|
@@ -273,9 +273,9 @@ export const ValidationUtils = {
|
|
|
273
273
|
},
|
|
274
274
|
|
|
275
275
|
/**
|
|
276
|
-
*
|
|
276
|
+
* Checks that a permit is signed and not expired.
|
|
277
277
|
*/
|
|
278
|
-
|
|
278
|
+
isSignedAndNotExpired: (permit: Permit): ValidationResult => {
|
|
279
279
|
if (ValidationUtils.isExpired(permit)) {
|
|
280
280
|
return { valid: false, error: 'expired' };
|
|
281
281
|
}
|
|
@@ -284,4 +284,44 @@ export const ValidationUtils = {
|
|
|
284
284
|
}
|
|
285
285
|
return { valid: true, error: null };
|
|
286
286
|
},
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Asserts that a permit is signed and not expired.
|
|
290
|
+
*
|
|
291
|
+
* Throws `Error` with message:
|
|
292
|
+
* - `Permit is expired`
|
|
293
|
+
* - `Permit is not signed`
|
|
294
|
+
*/
|
|
295
|
+
assertSignedAndNotExpired: (permit: Permit): void => {
|
|
296
|
+
const result = ValidationUtils.isSignedAndNotExpired(permit);
|
|
297
|
+
if (result.valid) return;
|
|
298
|
+
|
|
299
|
+
if (result.error === 'expired') {
|
|
300
|
+
throw new Error('Permit is expired');
|
|
301
|
+
}
|
|
302
|
+
if (result.error === 'not-signed') {
|
|
303
|
+
throw new Error('Permit is not signed');
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Should be unreachable, but keeps this future-proof.
|
|
307
|
+
throw new Error('Permit is invalid');
|
|
308
|
+
},
|
|
309
|
+
|
|
310
|
+
isValid: (permit: Permit): ValidationResult => {
|
|
311
|
+
const schema =
|
|
312
|
+
permit.type === 'self'
|
|
313
|
+
? SelfPermitValidator
|
|
314
|
+
: permit.type === 'sharing'
|
|
315
|
+
? SharingPermitValidator
|
|
316
|
+
: permit.type === 'recipient'
|
|
317
|
+
? ImportPermitValidator
|
|
318
|
+
: null;
|
|
319
|
+
|
|
320
|
+
if (schema == null) return { valid: false, error: 'invalid-schema' };
|
|
321
|
+
|
|
322
|
+
const schemaResult = schema.safeParse(permit);
|
|
323
|
+
if (!schemaResult.success) return { valid: false, error: 'invalid-schema' };
|
|
324
|
+
|
|
325
|
+
return ValidationUtils.isSignedAndNotExpired(permit);
|
|
326
|
+
},
|
|
287
327
|
};
|
package/web/const.ts
ADDED
package/web/index.ts
CHANGED
|
@@ -10,16 +10,18 @@ import {
|
|
|
10
10
|
type FheKeyDeserializer,
|
|
11
11
|
type EncryptableItem,
|
|
12
12
|
fheTypeToString,
|
|
13
|
+
TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT,
|
|
13
14
|
} from '@/core';
|
|
14
15
|
|
|
15
16
|
// Import web-specific storage (internal use only)
|
|
16
|
-
import { createWebStorage } from './storage.js';
|
|
17
|
+
import { createSsrStorage, createWebStorage } from './storage.js';
|
|
17
18
|
|
|
18
19
|
// Import worker manager
|
|
19
20
|
import { getWorkerManager, terminateWorker, areWorkersAvailable } from './workerManager.js';
|
|
20
21
|
|
|
21
22
|
// Import tfhe for web
|
|
22
23
|
import init, { init_panic_hook, TfheCompactPublicKey, ProvenCompactCiphertextList, CompactPkeCrs } from 'tfhe';
|
|
24
|
+
import { hasDOM } from './const';
|
|
23
25
|
|
|
24
26
|
/**
|
|
25
27
|
* Internal function to initialize TFHE for web
|
|
@@ -45,12 +47,20 @@ const fromHexString = (hexString: string): Uint8Array => {
|
|
|
45
47
|
return new Uint8Array(arr.map((byte) => parseInt(byte, 16)));
|
|
46
48
|
};
|
|
47
49
|
|
|
50
|
+
const _deserializeTfhePublicKey = (buff: string): TfheCompactPublicKey => {
|
|
51
|
+
return TfheCompactPublicKey.safe_deserialize(fromHexString(buff), TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const _deserializeCompactPkeCrs = (buff: string): CompactPkeCrs => {
|
|
55
|
+
return CompactPkeCrs.safe_deserialize(fromHexString(buff), TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT);
|
|
56
|
+
};
|
|
57
|
+
|
|
48
58
|
/**
|
|
49
59
|
* Serializer for TFHE public keys
|
|
50
60
|
* Validates that the buffer can be deserialized into a TfheCompactPublicKey
|
|
51
61
|
*/
|
|
52
62
|
const tfhePublicKeyDeserializer: FheKeyDeserializer = (buff: string): void => {
|
|
53
|
-
|
|
63
|
+
_deserializeTfhePublicKey(buff);
|
|
54
64
|
};
|
|
55
65
|
|
|
56
66
|
/**
|
|
@@ -58,7 +68,7 @@ const tfhePublicKeyDeserializer: FheKeyDeserializer = (buff: string): void => {
|
|
|
58
68
|
* Validates that the buffer can be deserialized into ZkCompactPkePublicParams
|
|
59
69
|
*/
|
|
60
70
|
const compactPkeCrsDeserializer: FheKeyDeserializer = (buff: string): void => {
|
|
61
|
-
|
|
71
|
+
_deserializeCompactPkeCrs(buff);
|
|
62
72
|
};
|
|
63
73
|
|
|
64
74
|
/**
|
|
@@ -66,9 +76,9 @@ const compactPkeCrsDeserializer: FheKeyDeserializer = (buff: string): void => {
|
|
|
66
76
|
* This is used internally by the SDK to create encrypted inputs
|
|
67
77
|
*/
|
|
68
78
|
const zkBuilderAndCrsGenerator: ZkBuilderAndCrsGenerator = (fhe: string, crs: string) => {
|
|
69
|
-
const fhePublicKey =
|
|
79
|
+
const fhePublicKey = _deserializeTfhePublicKey(fhe);
|
|
70
80
|
const zkBuilder = ProvenCompactCiphertextList.builder(fhePublicKey);
|
|
71
|
-
const zkCrs =
|
|
81
|
+
const zkCrs = _deserializeCompactPkeCrs(crs);
|
|
72
82
|
|
|
73
83
|
return { zkBuilder, zkCrs };
|
|
74
84
|
};
|
|
@@ -103,7 +113,8 @@ export function createCofheConfig(config: CofheInputConfig): CofheConfig {
|
|
|
103
113
|
return createCofheConfigBase({
|
|
104
114
|
environment: 'web',
|
|
105
115
|
...config,
|
|
106
|
-
fheKeyStorage:
|
|
116
|
+
fheKeyStorage:
|
|
117
|
+
config.fheKeyStorage === null ? null : config.fheKeyStorage ?? (hasDOM ? createWebStorage() : createSsrStorage()),
|
|
107
118
|
});
|
|
108
119
|
}
|
|
109
120
|
|
|
@@ -159,3 +170,6 @@ export function createCofheClientWithCustomWorker(
|
|
|
159
170
|
zkProveWorkerFn: customZkProveWorkerFn,
|
|
160
171
|
});
|
|
161
172
|
}
|
|
173
|
+
|
|
174
|
+
export { createSsrStorage };
|
|
175
|
+
export { hasDOM } from './const';
|
package/web/storage.ts
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import type { IStorage } from '@/core';
|
|
2
2
|
import { constructClient } from 'iframe-shared-storage';
|
|
3
|
+
import { hasDOM } from './const';
|
|
3
4
|
/**
|
|
4
|
-
* Creates a web storage implementation using IndexedDB
|
|
5
|
+
* Creates a web storage implementation using IndexedDB.
|
|
6
|
+
* Must only be called in a browser environment (requires `document` for iframe injection).
|
|
5
7
|
* @returns IStorage implementation for browser environments
|
|
6
8
|
*/
|
|
7
|
-
export const createWebStorage = (): IStorage => {
|
|
9
|
+
export const createWebStorage = (opts = { enableLog: false }): IStorage => {
|
|
10
|
+
if (!hasDOM) throw new Error('createWebStorage can only be used in a browser environment');
|
|
11
|
+
|
|
8
12
|
const client = constructClient({
|
|
9
13
|
iframe: {
|
|
10
14
|
src: 'https://iframe-shared-storage.vercel.app/hub.html',
|
|
11
15
|
messagingOptions: {
|
|
12
|
-
enableLog: 'both',
|
|
16
|
+
enableLog: opts.enableLog ? 'both' : undefined,
|
|
13
17
|
},
|
|
14
18
|
|
|
15
19
|
iframeReadyTimeoutMs: 30_000, // if the iframe is not initied during this interval AND a reuqest is made, such request will throw an error
|
|
@@ -32,3 +36,14 @@ export const createWebStorage = (): IStorage => {
|
|
|
32
36
|
},
|
|
33
37
|
};
|
|
34
38
|
};
|
|
39
|
+
|
|
40
|
+
export function createSsrStorage(): IStorage {
|
|
41
|
+
// TODO: consider doing something like wagmi's cookies storage for SSR - this in-memory storage will not persist across requests, but it also won't throw errors if accessed in SSR (e.g. during getServerSideProps in Next.js)
|
|
42
|
+
// https://wagmi.sh/react/guides/ssr#_1-set-up-cookie-storage
|
|
43
|
+
console.warn('using no-op server-side SSR storage');
|
|
44
|
+
return {
|
|
45
|
+
getItem: async () => null,
|
|
46
|
+
setItem: async () => {},
|
|
47
|
+
removeItem: async () => {},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -6,7 +6,7 @@ import { arbitrumSepolia as viemArbitrumSepolia } from 'viem/chains';
|
|
|
6
6
|
import type { PublicClient, WalletClient } from 'viem';
|
|
7
7
|
import { createPublicClient, createWalletClient, http } from 'viem';
|
|
8
8
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
9
|
-
import { createCofheClient, createCofheConfig } from '
|
|
9
|
+
import { createCofheClient, createCofheConfig } from '../index.js';
|
|
10
10
|
|
|
11
11
|
// Real test setup - runs in browser
|
|
12
12
|
const TEST_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';
|
|
@@ -141,7 +141,19 @@ describe('@cofhe/web - Client', () => {
|
|
|
141
141
|
expect(builder).toBeDefined();
|
|
142
142
|
expect(typeof builder.setChainId).toBe('function');
|
|
143
143
|
expect(typeof builder.setAccount).toBe('function');
|
|
144
|
+
expect(typeof builder.onPoll).toBe('function');
|
|
144
145
|
expect(typeof builder.execute).toBe('function');
|
|
145
146
|
}, 30000);
|
|
147
|
+
|
|
148
|
+
it('should create decryptForTx builder after connection', async () => {
|
|
149
|
+
await cofheClient.connect(publicClient, walletClient);
|
|
150
|
+
|
|
151
|
+
const builder = cofheClient.decryptForTx('0x123');
|
|
152
|
+
|
|
153
|
+
expect(builder).toBeDefined();
|
|
154
|
+
expect(typeof builder.setChainId).toBe('function');
|
|
155
|
+
expect(typeof builder.setAccount).toBe('function');
|
|
156
|
+
expect(typeof builder.onPoll).toBe('function');
|
|
157
|
+
}, 30000);
|
|
146
158
|
});
|
|
147
159
|
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { arbSepolia } from '@/chains';
|
|
2
|
+
|
|
3
|
+
import { describe, it, expect } from 'vitest';
|
|
4
|
+
import { createCofheConfig } from '../index.js';
|
|
5
|
+
|
|
6
|
+
describe('@cofhe/web - Config', () => {
|
|
7
|
+
it('should automatically inject IndexedDB storage as default', () => {
|
|
8
|
+
const config = createCofheConfig({
|
|
9
|
+
supportedChains: [arbSepolia],
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
expect(config.fheKeyStorage).toBeDefined();
|
|
13
|
+
expect(config.fheKeyStorage).not.toBeNull();
|
|
14
|
+
expect(config.supportedChains).toEqual([arbSepolia]);
|
|
15
|
+
});
|
|
16
|
+
});
|