@cofhe/sdk 1.0.0-alpha.0 → 1.0.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 (36) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/acps/onchain-utils.ts +15 -10
  3. package/acps/sealing.ts +22 -2
  4. package/acps/test/aclServedAddresses.test.ts +65 -0
  5. package/acps/test/sealing.test.ts +51 -0
  6. package/core/encrypt/encryptInputsBuilder.ts +31 -13
  7. package/core/encrypt/zkPackProveVerify.ts +16 -4
  8. package/core/error.ts +1 -0
  9. package/core/test/encryptInputsBuilder.test.ts +56 -4
  10. package/core/test/zkPoKMetadata.test.ts +27 -0
  11. package/dist/acps.cjs +122 -99
  12. package/dist/acps.d.cts +2 -2
  13. package/dist/acps.d.ts +2 -2
  14. package/dist/acps.js +2 -2
  15. package/dist/{chunk-6ZW76ZKB.js → chunk-3SD2UCRE.js} +1 -1
  16. package/dist/{chunk-OL3DZN7A.js → chunk-GKEJHZ52.js} +35 -19
  17. package/dist/{chunk-XQ32JG3T.js → chunk-OC5TGQGG.js} +14 -13
  18. package/dist/{chunk-BQWHPIYU.js → chunk-WAA2U7CT.js} +29 -6
  19. package/dist/{clientTypes-C0A9ji5O.d.cts → clientTypes-C2b_qub5.d.cts} +6 -3
  20. package/dist/{clientTypes-CcU5WvdV.d.ts → clientTypes-DEKYh0cw.d.ts} +6 -3
  21. package/dist/core.cjs +220 -180
  22. package/dist/core.d.cts +4 -3
  23. package/dist/core.d.ts +4 -3
  24. package/dist/core.js +4 -4
  25. package/dist/node.cjs +153 -114
  26. package/dist/node.d.cts +2 -2
  27. package/dist/node.d.ts +2 -2
  28. package/dist/node.js +4 -4
  29. package/dist/{onchain-utils-D9Z33IP7.d.cts → onchain-utils-CgOslubH.d.cts} +3 -3
  30. package/dist/{onchain-utils-D9Z33IP7.d.ts → onchain-utils-CgOslubH.d.ts} +3 -3
  31. package/dist/web.cjs +153 -114
  32. package/dist/web.d.cts +2 -2
  33. package/dist/web.d.ts +2 -2
  34. package/dist/web.js +4 -4
  35. package/dist/zkProve.worker.js +2 -2
  36. package/package.json +1 -1
package/dist/node.cjs CHANGED
@@ -44,6 +44,100 @@ function toBigInt(value) {
44
44
  }
45
45
  }
46
46
 
47
+ // core/error.ts
48
+ var CofheError = class _CofheError extends Error {
49
+ code;
50
+ cause;
51
+ hint;
52
+ context;
53
+ apiErrorCode;
54
+ constructor({ code, message, cause, hint, context, apiErrorCode }) {
55
+ const fullMessage = cause ? `${message} | Caused by: ${cause.message}` : message;
56
+ super(fullMessage);
57
+ this.name = "CofheError";
58
+ this.code = code;
59
+ this.cause = cause;
60
+ this.hint = hint;
61
+ this.context = context;
62
+ this.apiErrorCode = apiErrorCode;
63
+ if (Error.captureStackTrace) {
64
+ Error.captureStackTrace(this, _CofheError);
65
+ }
66
+ }
67
+ /**
68
+ * Creates a CofheError from an unknown error
69
+ * If the error is a CofheError, it is returned unchanged, else a new CofheError is created
70
+ * If a wrapperError is provided, it is used to create the new CofheError, else a default is used
71
+ */
72
+ static fromError(error, wrapperError) {
73
+ if (isCofheError(error))
74
+ return error;
75
+ const cause = error instanceof Error ? error : new Error(`${error}`);
76
+ return new _CofheError({
77
+ code: wrapperError?.code ?? "INTERNAL_ERROR" /* InternalError */,
78
+ message: wrapperError?.message ?? "An internal error occurred",
79
+ hint: wrapperError?.hint,
80
+ context: wrapperError?.context,
81
+ apiErrorCode: wrapperError?.apiErrorCode,
82
+ cause
83
+ });
84
+ }
85
+ /**
86
+ * Serializes the error to JSON string with proper handling of Error objects
87
+ */
88
+ serialize() {
89
+ return bigintSafeJsonStringify({
90
+ name: this.name,
91
+ code: this.code,
92
+ message: this.message,
93
+ hint: this.hint,
94
+ context: this.context,
95
+ apiErrorCode: this.apiErrorCode,
96
+ cause: this.cause ? {
97
+ name: this.cause.name,
98
+ message: this.cause.message,
99
+ stack: this.cause.stack
100
+ } : void 0,
101
+ stack: this.stack
102
+ });
103
+ }
104
+ /**
105
+ * Returns a human-readable string representation of the error
106
+ */
107
+ toString() {
108
+ const codeSuffix = this.apiErrorCode ? ` (api: ${this.apiErrorCode})` : "";
109
+ const parts = [`${this.name} [${this.code}]${codeSuffix}: ${this.message}`];
110
+ if (this.hint) {
111
+ parts.push(`Hint: ${this.hint}`);
112
+ }
113
+ if (this.context && Object.keys(this.context).length > 0) {
114
+ parts.push(`Context: ${bigintSafeJsonStringify(this.context)}`);
115
+ }
116
+ if (this.stack) {
117
+ parts.push(`
118
+ Stack trace:`);
119
+ parts.push(this.stack);
120
+ }
121
+ if (this.cause) {
122
+ parts.push(`
123
+ Caused by: ${this.cause.name}: ${this.cause.message}`);
124
+ if (this.cause.stack) {
125
+ parts.push(this.cause.stack);
126
+ }
127
+ }
128
+ return parts.join("\n");
129
+ }
130
+ };
131
+ var bigintSafeJsonStringify = (value) => {
132
+ return JSON.stringify(value, (key, value2) => {
133
+ if (typeof value2 === "bigint") {
134
+ return `${value2}n`;
135
+ }
136
+ return value2;
137
+ });
138
+ };
139
+ var isCofheError = (error) => error instanceof CofheError;
140
+
47
141
  // acps/sealing.ts
48
142
  var KEY_HEX_LENGTH = 64;
49
143
  var GenerateSealingKey = () => {
@@ -58,9 +152,25 @@ var unsealWithPrivateKey = (privateKey, parsedData) => {
58
152
  const nonce = parsedData.nonce instanceof Uint8Array ? parsedData.nonce : new Uint8Array(parsedData.nonce);
59
153
  const ephemPublicKey = parsedData.public_key instanceof Uint8Array ? parsedData.public_key : new Uint8Array(parsedData.public_key);
60
154
  const dataToDecrypt = parsedData.data instanceof Uint8Array ? parsedData.data : new Uint8Array(parsedData.data);
61
- const decryptedMessage = nacl__default.default.box.open(dataToDecrypt, nonce, ephemPublicKey, fromHexString(privateKey));
155
+ let decryptedMessage;
156
+ let cause;
157
+ try {
158
+ decryptedMessage = nacl__default.default.box.open(dataToDecrypt, nonce, ephemPublicKey, fromHexString(privateKey));
159
+ } catch (e) {
160
+ decryptedMessage = null;
161
+ cause = e instanceof Error ? e : void 0;
162
+ }
62
163
  if (!decryptedMessage) {
63
- throw new Error("Failed to decrypt message");
164
+ throw new CofheError({
165
+ code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
166
+ message: "Failed to unseal decrypt response",
167
+ cause,
168
+ context: {
169
+ dataLength: dataToDecrypt.length,
170
+ nonceLength: nonce.length,
171
+ publicKeyLength: ephemPublicKey.length
172
+ }
173
+ });
64
174
  }
65
175
  return toBigInt(decryptedMessage);
66
176
  };
@@ -378,100 +488,6 @@ var TFHE_RS_ZK_MAX_BITS = 2048;
378
488
  var TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT = BigInt(1 << 30);
379
489
  var TFHE_RS_KEY_VERSION = 2;
380
490
 
381
- // core/error.ts
382
- var CofheError = class _CofheError extends Error {
383
- code;
384
- cause;
385
- hint;
386
- context;
387
- apiErrorCode;
388
- constructor({ code, message, cause, hint, context, apiErrorCode }) {
389
- const fullMessage = cause ? `${message} | Caused by: ${cause.message}` : message;
390
- super(fullMessage);
391
- this.name = "CofheError";
392
- this.code = code;
393
- this.cause = cause;
394
- this.hint = hint;
395
- this.context = context;
396
- this.apiErrorCode = apiErrorCode;
397
- if (Error.captureStackTrace) {
398
- Error.captureStackTrace(this, _CofheError);
399
- }
400
- }
401
- /**
402
- * Creates a CofheError from an unknown error
403
- * If the error is a CofheError, it is returned unchanged, else a new CofheError is created
404
- * If a wrapperError is provided, it is used to create the new CofheError, else a default is used
405
- */
406
- static fromError(error, wrapperError) {
407
- if (isCofheError(error))
408
- return error;
409
- const cause = error instanceof Error ? error : new Error(`${error}`);
410
- return new _CofheError({
411
- code: wrapperError?.code ?? "INTERNAL_ERROR" /* InternalError */,
412
- message: wrapperError?.message ?? "An internal error occurred",
413
- hint: wrapperError?.hint,
414
- context: wrapperError?.context,
415
- apiErrorCode: wrapperError?.apiErrorCode,
416
- cause
417
- });
418
- }
419
- /**
420
- * Serializes the error to JSON string with proper handling of Error objects
421
- */
422
- serialize() {
423
- return bigintSafeJsonStringify({
424
- name: this.name,
425
- code: this.code,
426
- message: this.message,
427
- hint: this.hint,
428
- context: this.context,
429
- apiErrorCode: this.apiErrorCode,
430
- cause: this.cause ? {
431
- name: this.cause.name,
432
- message: this.cause.message,
433
- stack: this.cause.stack
434
- } : void 0,
435
- stack: this.stack
436
- });
437
- }
438
- /**
439
- * Returns a human-readable string representation of the error
440
- */
441
- toString() {
442
- const codeSuffix = this.apiErrorCode ? ` (api: ${this.apiErrorCode})` : "";
443
- const parts = [`${this.name} [${this.code}]${codeSuffix}: ${this.message}`];
444
- if (this.hint) {
445
- parts.push(`Hint: ${this.hint}`);
446
- }
447
- if (this.context && Object.keys(this.context).length > 0) {
448
- parts.push(`Context: ${bigintSafeJsonStringify(this.context)}`);
449
- }
450
- if (this.stack) {
451
- parts.push(`
452
- Stack trace:`);
453
- parts.push(this.stack);
454
- }
455
- if (this.cause) {
456
- parts.push(`
457
- Caused by: ${this.cause.name}: ${this.cause.message}`);
458
- if (this.cause.stack) {
459
- parts.push(this.cause.stack);
460
- }
461
- }
462
- return parts.join("\n");
463
- }
464
- };
465
- var bigintSafeJsonStringify = (value) => {
466
- return JSON.stringify(value, (key, value2) => {
467
- if (typeof value2 === "bigint") {
468
- return `${value2}n`;
469
- }
470
- return value2;
471
- });
472
- };
473
- var isCofheError = (error) => error instanceof CofheError;
474
-
475
491
  // core/taskManager.ts
476
492
  var ADDRESS_BOOK_ABI = viem.parseAbi([
477
493
  "function getTm(uint256 id) view returns (address)",
@@ -779,15 +795,22 @@ var getAclServedAddresses = async (publicClient, chainId) => {
779
795
  } catch {
780
796
  return {};
781
797
  }
798
+ let transient = false;
799
+ const readServedAddress = (functionName) => publicClient.readContract({ address: aclAddress, abi: ACL_SERVED_ADDRESSES_ABI, functionName }).catch((err) => {
800
+ if (revertErrorName(err, ACL_SERVED_ADDRESSES_ABI) === void 0)
801
+ transient = true;
802
+ return void 0;
803
+ });
782
804
  const [defaultRevoker, shareRegistry] = await Promise.all([
783
- publicClient.readContract({ address: aclAddress, abi: ACL_SERVED_ADDRESSES_ABI, functionName: "defaultRevokerContract" }).catch(() => void 0),
784
- publicClient.readContract({ address: aclAddress, abi: ACL_SERVED_ADDRESSES_ABI, functionName: "shareRegistry" }).catch(() => void 0)
805
+ readServedAddress("defaultRevokerContract"),
806
+ readServedAddress("shareRegistry")
785
807
  ]);
786
808
  const resolved = {
787
809
  defaultRevoker: defaultRevoker != null && defaultRevoker !== viem.zeroAddress ? defaultRevoker : void 0,
788
810
  shareRegistry: shareRegistry != null && shareRegistry !== viem.zeroAddress ? shareRegistry : void 0
789
811
  };
790
- aclServedAddressesCache.set(chainId, resolved);
812
+ if (!transient)
813
+ aclServedAddressesCache.set(chainId, resolved);
791
814
  return resolved;
792
815
  };
793
816
 
@@ -1902,19 +1925,22 @@ var zkProve = async (builder, crs, metadata) => {
1902
1925
  }, 0);
1903
1926
  });
1904
1927
  };
1905
- var constructZkPoKMetadata = (accountAddr, securityZone, chainId) => {
1928
+ var constructZkPoKMetadata = (accountAddr, securityZone, chainId, consumingContract) => {
1906
1929
  const accountAddrNoPrefix = accountAddr.startsWith("0x") ? accountAddr.slice(2) : accountAddr;
1907
1930
  const accountBytes = hexToBytes(accountAddrNoPrefix);
1931
+ const contractNoPrefix = consumingContract.startsWith("0x") ? consumingContract.slice(2) : consumingContract;
1932
+ const contractBytes = hexToBytes(contractNoPrefix);
1908
1933
  const chainIdBytes = new Uint8Array(32);
1909
1934
  let value = chainId;
1910
1935
  for (let i = 31; i >= 0 && value > 0; i--) {
1911
1936
  chainIdBytes[i] = value & 255;
1912
1937
  value = value >>> 8;
1913
1938
  }
1914
- const metadata = new Uint8Array(1 + accountBytes.length + 32);
1939
+ const metadata = new Uint8Array(1 + accountBytes.length + 32 + contractBytes.length);
1915
1940
  metadata[0] = securityZone;
1916
1941
  metadata.set(accountBytes, 1);
1917
1942
  metadata.set(chainIdBytes, 1 + accountBytes.length);
1943
+ metadata.set(contractBytes, 1 + accountBytes.length + 32);
1918
1944
  return metadata;
1919
1945
  };
1920
1946
  var zkVerifyBatch = async (verifierUrl, serializedBytes, address, securityZone, chainId, consumingContract) => {
@@ -2827,20 +2853,33 @@ var EncryptInputsBuilder = class extends BaseBuilder {
2827
2853
  return this.consumingContract;
2828
2854
  }
2829
2855
  /**
2830
- * Asserts that this.consumingContract is populated
2831
- * @throws {CofheError} If consumingContract is not set
2856
+ * Asserts that this.consumingContract is populated and is an address.
2857
+ * Runs before TFHE init, key fetching and proving, so a bad address costs
2858
+ * nothing: the contract is bound into the proof, and a proof built for a
2859
+ * non-address could only fail at the verifier, after the expensive step.
2860
+ * @throws {CofheError} If consumingContract is not set or is not an address
2832
2861
  */
2833
2862
  assertConsumingContract() {
2834
- if (this.consumingContract)
2835
- return;
2836
- throw new CofheError({
2837
- code: "CONSUMING_CONTRACT_UNINITIALIZED" /* ConsumingContractUninitialized */,
2838
- message: "Consuming contract is not set",
2839
- hint: "Use setConsumingContract(...) to set the contract that will consume the encrypted inputs.",
2840
- context: {
2841
- consumingContract: this.consumingContract
2842
- }
2843
- });
2863
+ if (!this.consumingContract) {
2864
+ throw new CofheError({
2865
+ code: "CONSUMING_CONTRACT_UNINITIALIZED" /* ConsumingContractUninitialized */,
2866
+ message: "Consuming contract is not set",
2867
+ hint: "Use setConsumingContract(...) to set the contract that will consume the encrypted inputs.",
2868
+ context: {
2869
+ consumingContract: this.consumingContract
2870
+ }
2871
+ });
2872
+ }
2873
+ if (!viem.isAddress(this.consumingContract, { strict: false })) {
2874
+ throw new CofheError({
2875
+ code: "CONSUMING_CONTRACT_INVALID" /* ConsumingContractInvalid */,
2876
+ message: "Consuming contract is not an address",
2877
+ hint: "setConsumingContract(...) takes the 20-byte address of the contract that will consume the encrypted inputs.",
2878
+ context: {
2879
+ consumingContract: this.consumingContract
2880
+ }
2881
+ });
2882
+ }
2844
2883
  }
2845
2884
  setUseWorker(useWorker) {
2846
2885
  this.useWorker = useWorker;
@@ -3056,7 +3095,7 @@ var EncryptInputsBuilder = class extends BaseBuilder {
3056
3095
  zkBuilder = zkPack(this.inputItems, zkBuilder);
3057
3096
  this.fireStepEnd("pack" /* Pack */);
3058
3097
  this.fireStepStart("prove" /* Prove */);
3059
- const metadata = constructZkPoKMetadata(this.account, this.securityZone, this.chainId);
3098
+ const metadata = constructZkPoKMetadata(this.account, this.securityZone, this.chainId, this.consumingContract);
3060
3099
  let proof = null;
3061
3100
  let usedWorker = false;
3062
3101
  let workerFailedError;
package/dist/node.d.cts CHANGED
@@ -1,8 +1,8 @@
1
- import { C as CofheInputConfig, a as CofheConfig, b as CofheClient } from './clientTypes-C0A9ji5O.cjs';
1
+ import { C as CofheInputConfig, a as CofheConfig, b as CofheClient } from './clientTypes-C2b_qub5.cjs';
2
2
  import 'viem';
3
3
  import './types-C07FK-cL.cjs';
4
4
  import 'zod';
5
- import './onchain-utils-D9Z33IP7.cjs';
5
+ import './onchain-utils-CgOslubH.cjs';
6
6
  import 'zustand/vanilla';
7
7
 
8
8
  /**
package/dist/node.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { C as CofheInputConfig, a as CofheConfig, b as CofheClient } from './clientTypes-CcU5WvdV.js';
1
+ import { C as CofheInputConfig, a as CofheConfig, b as CofheClient } from './clientTypes-DEKYh0cw.js';
2
2
  import 'viem';
3
3
  import './types-C07FK-cL.js';
4
4
  import 'zod';
5
- import './onchain-utils-D9Z33IP7.js';
5
+ import './onchain-utils-CgOslubH.js';
6
6
  import 'zustand/vanilla';
7
7
 
8
8
  /**
package/dist/node.js CHANGED
@@ -1,8 +1,8 @@
1
- import { createCofheConfigBase, createCofheClientBase } from './chunk-OL3DZN7A.js';
1
+ import { createCofheConfigBase, createCofheClientBase } from './chunk-GKEJHZ52.js';
2
2
  import './chunk-UKQAJK4N.js';
3
- import './chunk-BQWHPIYU.js';
4
- import { fromHexString } from './chunk-6ZW76ZKB.js';
5
- import { TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT } from './chunk-XQ32JG3T.js';
3
+ import './chunk-WAA2U7CT.js';
4
+ import { fromHexString } from './chunk-3SD2UCRE.js';
5
+ import { TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT } from './chunk-OC5TGQGG.js';
6
6
  import { promises } from 'fs';
7
7
  import { join } from 'path';
8
8
  import { init_panic_hook, ProvenCompactCiphertextList, CompactPkeCrs, TfheCompactPublicKey } from 'node-tfhe';
@@ -552,9 +552,9 @@ declare const clearAclServedAddresses: () => void;
552
552
  * TaskManager -> acl() -> getters). Zero addresses and pre-upgrade ACLs (getters absent -> revert)
553
553
  * resolve to `undefined` — callers fall back to `acp.*` config.
554
554
  *
555
- * Resolutions are cached per chainId. A failure to reach the TaskManager (network
556
- * error, no CoFHE deployment) is NOT cached, so a transient outage does not pin
557
- * an empty result for the whole session.
555
+ * Resolutions are cached per chainId. A failure to reach the TaskManager or the ACL
556
+ * getters (network error, no CoFHE deployment) is NOT cached, so a transient outage
557
+ * does not pin an empty result for the whole session.
558
558
  */
559
559
  declare const getAclServedAddresses: (publicClient: PublicClient, chainId: number) => Promise<AclServedAddresses>;
560
560
 
@@ -552,9 +552,9 @@ declare const clearAclServedAddresses: () => void;
552
552
  * TaskManager -> acl() -> getters). Zero addresses and pre-upgrade ACLs (getters absent -> revert)
553
553
  * resolve to `undefined` — callers fall back to `acp.*` config.
554
554
  *
555
- * Resolutions are cached per chainId. A failure to reach the TaskManager (network
556
- * error, no CoFHE deployment) is NOT cached, so a transient outage does not pin
557
- * an empty result for the whole session.
555
+ * Resolutions are cached per chainId. A failure to reach the TaskManager or the ACL
556
+ * getters (network error, no CoFHE deployment) is NOT cached, so a transient outage
557
+ * does not pin an empty result for the whole session.
558
558
  */
559
559
  declare const getAclServedAddresses: (publicClient: PublicClient, chainId: number) => Promise<AclServedAddresses>;
560
560