@cofhe/sdk 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/adapters/ethers6.ts +28 -28
  3. package/adapters/hardhat.ts +0 -1
  4. package/adapters/index.test.ts +14 -19
  5. package/adapters/smartWallet.ts +81 -73
  6. package/adapters/test-utils.ts +45 -45
  7. package/adapters/types.ts +3 -3
  8. package/chains/chains/localcofhe.ts +14 -0
  9. package/chains/chains.test.ts +2 -1
  10. package/chains/index.ts +3 -1
  11. package/core/baseBuilder.ts +30 -49
  12. package/core/client.test.ts +94 -77
  13. package/core/client.ts +133 -149
  14. package/core/clientTypes.ts +108 -0
  15. package/core/config.test.ts +22 -11
  16. package/core/config.ts +16 -9
  17. package/core/decrypt/decryptHandleBuilder.ts +51 -45
  18. package/core/decrypt/{tnSealOutput.ts → tnSealOutputV1.ts} +1 -1
  19. package/core/decrypt/tnSealOutputV2.ts +298 -0
  20. package/core/encrypt/cofheMocksZkVerifySign.ts +16 -10
  21. package/core/encrypt/encryptInputsBuilder.test.ts +132 -116
  22. package/core/encrypt/encryptInputsBuilder.ts +159 -111
  23. package/core/encrypt/encryptUtils.ts +6 -3
  24. package/core/encrypt/zkPackProveVerify.ts +70 -8
  25. package/core/error.ts +0 -2
  26. package/core/fetchKeys.test.ts +1 -18
  27. package/core/fetchKeys.ts +0 -26
  28. package/core/index.ts +29 -17
  29. package/core/keyStore.ts +65 -38
  30. package/core/permits.test.ts +253 -1
  31. package/core/permits.ts +80 -16
  32. package/core/types.ts +198 -152
  33. package/core/utils.ts +43 -1
  34. package/dist/adapters.d.cts +38 -20
  35. package/dist/adapters.d.ts +38 -20
  36. package/dist/chains.cjs +14 -1
  37. package/dist/chains.d.cts +23 -1
  38. package/dist/chains.d.ts +23 -1
  39. package/dist/chains.js +1 -1
  40. package/dist/{chunk-LU7BMUUT.js → chunk-UGBVZNRT.js} +39 -25
  41. package/dist/{chunk-GZCQQYVI.js → chunk-WEAZ25JO.js} +14 -2
  42. package/dist/{chunk-KFGPTJ6X.js → chunk-WGCRJCBR.js} +1920 -1692
  43. package/dist/{types-bB7wLj0q.d.cts → clientTypes-5_1nwtUe.d.cts} +308 -347
  44. package/dist/{types-PhwGgQvs.d.ts → clientTypes-Es7fyi65.d.ts} +308 -347
  45. package/dist/core.cjs +2872 -2632
  46. package/dist/core.d.cts +101 -6
  47. package/dist/core.d.ts +101 -6
  48. package/dist/core.js +3 -3
  49. package/dist/node.cjs +2716 -2520
  50. package/dist/node.d.cts +3 -3
  51. package/dist/node.d.ts +3 -3
  52. package/dist/node.js +4 -3
  53. package/dist/{permit-S9CnI6MF.d.cts → permit-fUSe6KKq.d.cts} +31 -15
  54. package/dist/{permit-S9CnI6MF.d.ts → permit-fUSe6KKq.d.ts} +31 -15
  55. package/dist/permits.cjs +39 -24
  56. package/dist/permits.d.cts +137 -148
  57. package/dist/permits.d.ts +137 -148
  58. package/dist/permits.js +1 -1
  59. package/dist/web.cjs +2929 -2518
  60. package/dist/web.d.cts +21 -5
  61. package/dist/web.d.ts +21 -5
  62. package/dist/web.js +185 -9
  63. package/dist/zkProve.worker.cjs +93 -0
  64. package/dist/zkProve.worker.d.cts +2 -0
  65. package/dist/zkProve.worker.d.ts +2 -0
  66. package/dist/zkProve.worker.js +91 -0
  67. package/node/client.test.ts +20 -25
  68. package/node/encryptInputs.test.ts +18 -38
  69. package/node/index.ts +1 -0
  70. package/package.json +14 -14
  71. package/permits/index.ts +1 -0
  72. package/permits/localstorage.test.ts +0 -1
  73. package/permits/permit.test.ts +25 -22
  74. package/permits/permit.ts +30 -21
  75. package/permits/sealing.test.ts +3 -3
  76. package/permits/sealing.ts +2 -2
  77. package/permits/store.ts +5 -7
  78. package/permits/test-utils.ts +1 -1
  79. package/permits/types.ts +17 -0
  80. package/permits/utils.ts +0 -1
  81. package/permits/validation.ts +24 -4
  82. package/web/client.web.test.ts +20 -25
  83. package/web/config.web.test.ts +0 -2
  84. package/web/encryptInputs.web.test.ts +31 -54
  85. package/web/index.ts +65 -1
  86. package/web/storage.ts +19 -5
  87. package/web/worker.builder.web.test.ts +148 -0
  88. package/web/worker.config.web.test.ts +329 -0
  89. package/web/worker.output.web.test.ts +84 -0
  90. package/web/workerManager.test.ts +80 -0
  91. package/web/workerManager.ts +214 -0
  92. package/web/workerManager.web.test.ts +114 -0
  93. package/web/zkProve.worker.ts +133 -0
  94. package/core/result.test.ts +0 -180
  95. package/core/result.ts +0 -67
  96. package/core/test-utils.ts +0 -45
@@ -0,0 +1,298 @@
1
+ import { type Permission, type EthEncryptedData } from '@/permits';
2
+
3
+ import { CofhesdkError, CofhesdkErrorCode } from '../error.js';
4
+
5
+ // Polling configuration
6
+ const POLL_INTERVAL_MS = 1000; // 1 second
7
+ const POLL_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
8
+
9
+ // V2 API response types
10
+ type SealOutputSubmitResponse = {
11
+ request_id: string;
12
+ };
13
+
14
+ type SealOutputStatusResponse = {
15
+ request_id: string;
16
+ status: 'PROCESSING' | 'COMPLETED';
17
+ submitted_at: string;
18
+ completed_at?: string;
19
+ is_succeed?: boolean;
20
+ sealed?: {
21
+ data: number[];
22
+ public_key: number[];
23
+ nonce: number[];
24
+ };
25
+ signature?: string;
26
+ encryption_type?: number;
27
+ error_message?: string | null;
28
+ };
29
+
30
+ /**
31
+ * Converts a number array to Uint8Array
32
+ */
33
+ function numberArrayToUint8Array(arr: number[]): Uint8Array {
34
+ return new Uint8Array(arr);
35
+ }
36
+
37
+ /**
38
+ * Converts the sealed data from the API response to EthEncryptedData
39
+ */
40
+ function convertSealedData(sealed: SealOutputStatusResponse['sealed']): EthEncryptedData {
41
+ if (!sealed) {
42
+ throw new CofhesdkError({
43
+ code: CofhesdkErrorCode.SealOutputReturnedNull,
44
+ message: 'Sealed data is missing from completed response',
45
+ });
46
+ }
47
+
48
+ return {
49
+ data: numberArrayToUint8Array(sealed.data),
50
+ public_key: numberArrayToUint8Array(sealed.public_key),
51
+ nonce: numberArrayToUint8Array(sealed.nonce),
52
+ };
53
+ }
54
+
55
+ /**
56
+ * Submits a sealoutput request to the v2 API and returns the request_id
57
+ */
58
+ async function submitSealOutputRequest(
59
+ thresholdNetworkUrl: string,
60
+ ctHash: bigint,
61
+ chainId: number,
62
+ permission: Permission
63
+ ): Promise<string> {
64
+ const body = {
65
+ ct_tempkey: ctHash.toString(16).padStart(64, '0'),
66
+ host_chain_id: chainId,
67
+ permit: permission,
68
+ };
69
+
70
+ let response: Response;
71
+ try {
72
+ response = await fetch(`${thresholdNetworkUrl}/v2/sealoutput`, {
73
+ method: 'POST',
74
+ headers: {
75
+ 'Content-Type': 'application/json',
76
+ },
77
+ body: JSON.stringify(body),
78
+ });
79
+ } catch (e) {
80
+ throw new CofhesdkError({
81
+ code: CofhesdkErrorCode.SealOutputFailed,
82
+ message: `sealOutput request failed`,
83
+ hint: 'Ensure the threshold network URL is valid and reachable.',
84
+ cause: e instanceof Error ? e : undefined,
85
+ context: {
86
+ thresholdNetworkUrl,
87
+ body,
88
+ },
89
+ });
90
+ }
91
+
92
+ // Handle non-200 status codes
93
+ if (!response.ok) {
94
+ let errorMessage = `HTTP ${response.status}`;
95
+ try {
96
+ const errorBody = await response.json();
97
+ errorMessage = errorBody.error_message || errorBody.message || errorMessage;
98
+ } catch {
99
+ // Ignore JSON parse errors, use status text
100
+ errorMessage = response.statusText || errorMessage;
101
+ }
102
+
103
+ throw new CofhesdkError({
104
+ code: CofhesdkErrorCode.SealOutputFailed,
105
+ message: `sealOutput request failed: ${errorMessage}`,
106
+ hint: 'Check the threshold network URL and request parameters.',
107
+ context: {
108
+ thresholdNetworkUrl,
109
+ status: response.status,
110
+ statusText: response.statusText,
111
+ body,
112
+ },
113
+ });
114
+ }
115
+
116
+ let submitResponse: SealOutputSubmitResponse;
117
+ try {
118
+ submitResponse = (await response.json()) as SealOutputSubmitResponse;
119
+ } catch (e) {
120
+ throw new CofhesdkError({
121
+ code: CofhesdkErrorCode.SealOutputFailed,
122
+ message: `Failed to parse sealOutput submit response`,
123
+ cause: e instanceof Error ? e : undefined,
124
+ context: {
125
+ thresholdNetworkUrl,
126
+ body,
127
+ },
128
+ });
129
+ }
130
+
131
+ if (!submitResponse.request_id) {
132
+ throw new CofhesdkError({
133
+ code: CofhesdkErrorCode.SealOutputFailed,
134
+ message: `sealOutput submit response missing request_id`,
135
+ context: {
136
+ thresholdNetworkUrl,
137
+ body,
138
+ submitResponse,
139
+ },
140
+ });
141
+ }
142
+
143
+ return submitResponse.request_id;
144
+ }
145
+
146
+ /**
147
+ * Polls for the sealoutput status until completed or timeout
148
+ */
149
+ async function pollSealOutputStatus(thresholdNetworkUrl: string, requestId: string): Promise<EthEncryptedData> {
150
+ const startTime = Date.now();
151
+ let completed = false;
152
+
153
+ while (!completed) {
154
+ // Check timeout
155
+ if (Date.now() - startTime > POLL_TIMEOUT_MS) {
156
+ throw new CofhesdkError({
157
+ code: CofhesdkErrorCode.SealOutputFailed,
158
+ message: `sealOutput polling timed out after ${POLL_TIMEOUT_MS}ms`,
159
+ hint: 'The request may still be processing. Try again later.',
160
+ context: {
161
+ thresholdNetworkUrl,
162
+ requestId,
163
+ timeoutMs: POLL_TIMEOUT_MS,
164
+ },
165
+ });
166
+ }
167
+
168
+ let response: Response;
169
+ try {
170
+ response = await fetch(`${thresholdNetworkUrl}/v2/sealoutput/${requestId}`, {
171
+ method: 'GET',
172
+ headers: {
173
+ 'Content-Type': 'application/json',
174
+ },
175
+ });
176
+ } catch (e) {
177
+ throw new CofhesdkError({
178
+ code: CofhesdkErrorCode.SealOutputFailed,
179
+ message: `sealOutput status poll failed`,
180
+ hint: 'Ensure the threshold network URL is valid and reachable.',
181
+ cause: e instanceof Error ? e : undefined,
182
+ context: {
183
+ thresholdNetworkUrl,
184
+ requestId,
185
+ },
186
+ });
187
+ }
188
+
189
+ // Handle 404 - request not found
190
+ if (response.status === 404) {
191
+ throw new CofhesdkError({
192
+ code: CofhesdkErrorCode.SealOutputFailed,
193
+ message: `sealOutput request not found: ${requestId}`,
194
+ hint: 'The request may have expired or been invalid.',
195
+ context: {
196
+ thresholdNetworkUrl,
197
+ requestId,
198
+ },
199
+ });
200
+ }
201
+
202
+ // Handle other non-200 status codes
203
+ if (!response.ok) {
204
+ let errorMessage = `HTTP ${response.status}`;
205
+ try {
206
+ const errorBody = await response.json();
207
+ errorMessage = errorBody.error_message || errorBody.message || errorMessage;
208
+ } catch {
209
+ errorMessage = response.statusText || errorMessage;
210
+ }
211
+
212
+ throw new CofhesdkError({
213
+ code: CofhesdkErrorCode.SealOutputFailed,
214
+ message: `sealOutput status poll failed: ${errorMessage}`,
215
+ context: {
216
+ thresholdNetworkUrl,
217
+ requestId,
218
+ status: response.status,
219
+ statusText: response.statusText,
220
+ },
221
+ });
222
+ }
223
+
224
+ let statusResponse: SealOutputStatusResponse;
225
+ try {
226
+ statusResponse = (await response.json()) as SealOutputStatusResponse;
227
+ } catch (e) {
228
+ throw new CofhesdkError({
229
+ code: CofhesdkErrorCode.SealOutputFailed,
230
+ message: `Failed to parse sealOutput status response`,
231
+ cause: e instanceof Error ? e : undefined,
232
+ context: {
233
+ thresholdNetworkUrl,
234
+ requestId,
235
+ },
236
+ });
237
+ }
238
+
239
+ // Check if completed
240
+ if (statusResponse.status === 'COMPLETED') {
241
+ // Check if succeeded
242
+ if (statusResponse.is_succeed === false) {
243
+ const errorMessage = statusResponse.error_message || 'Unknown error';
244
+ throw new CofhesdkError({
245
+ code: CofhesdkErrorCode.SealOutputFailed,
246
+ message: `sealOutput request failed: ${errorMessage}`,
247
+ context: {
248
+ thresholdNetworkUrl,
249
+ requestId,
250
+ statusResponse,
251
+ },
252
+ });
253
+ }
254
+
255
+ // Check if sealed data exists
256
+ if (!statusResponse.sealed) {
257
+ throw new CofhesdkError({
258
+ code: CofhesdkErrorCode.SealOutputReturnedNull,
259
+ message: `sealOutput request completed but returned no sealed data`,
260
+ context: {
261
+ thresholdNetworkUrl,
262
+ requestId,
263
+ statusResponse,
264
+ },
265
+ });
266
+ }
267
+
268
+ // Convert and return the sealed data
269
+ return convertSealedData(statusResponse.sealed);
270
+ }
271
+
272
+ // Still processing, wait before next poll
273
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
274
+ }
275
+
276
+ // This should never be reached, but TypeScript requires it
277
+ throw new CofhesdkError({
278
+ code: CofhesdkErrorCode.SealOutputFailed,
279
+ message: 'Polling loop exited unexpectedly',
280
+ context: {
281
+ thresholdNetworkUrl,
282
+ requestId,
283
+ },
284
+ });
285
+ }
286
+
287
+ export async function tnSealOutputV2(
288
+ ctHash: bigint,
289
+ chainId: number,
290
+ permission: Permission,
291
+ thresholdNetworkUrl: string
292
+ ): Promise<EthEncryptedData> {
293
+ // Step 1: Submit the request and get request_id
294
+ const requestId = await submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, permission);
295
+
296
+ // Step 2: Poll for status until completed
297
+ return await pollSealOutputStatus(thresholdNetworkUrl, requestId);
298
+ }
@@ -17,13 +17,23 @@ import { privateKeyToAccount } from 'viem/accounts';
17
17
 
18
18
  // Address the Mock ZkVerifier contract is deployed to on the Hardhat chain
19
19
  export const MocksZkVerifierAddress = '0x0000000000000000000000000000000000000100';
20
- // Private key of the account expected to sign the encrypted inputs
21
- export const MocksEncryptedInputSignerPkey = '0x6c8d7f768a6bb4aafe85e8a2f5a9680355239c7e14646ed62b044e39de154512';
20
+
21
+ // PK & address pair for zk verifier
22
+ export const MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY =
23
+ '0x6C8D7F768A6BB4AAFE85E8A2F5A9680355239C7E14646ED62B044E39DE154512';
24
+ export const MOCKS_ZK_VERIFIER_SIGNER_ADDRESS = '0x6E12D8C87503D4287c294f2Fdef96ACd9DFf6bd2';
22
25
 
23
26
  type EncryptableItemWithCtHash = EncryptableItem & {
24
27
  ctHash: bigint;
25
28
  };
26
29
 
30
+ function createMockZkVerifierSigner() {
31
+ return createWalletClient({
32
+ chain: hardhat,
33
+ transport: http(),
34
+ account: privateKeyToAccount(MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY),
35
+ });
36
+ }
27
37
  /**
28
38
  * The mocks don't use a tfhe builder, so we check the encryptable bits here to preserve parity
29
39
  */
@@ -187,18 +197,14 @@ async function createProofSignatures(items: EncryptableItemWithCtHash[], securit
187
197
  let encInputSignerClient: WalletClient | undefined;
188
198
 
189
199
  try {
190
- encInputSignerClient = createWalletClient({
191
- chain: hardhat,
192
- transport: http(),
193
- account: privateKeyToAccount(MocksEncryptedInputSignerPkey),
194
- });
200
+ encInputSignerClient = createMockZkVerifierSigner();
195
201
  } catch (err) {
196
202
  throw new CofhesdkError({
197
203
  code: CofhesdkErrorCode.ZkMocksCreateProofSignatureFailed,
198
204
  message: `mockZkVerifySign createProofSignatures failed while creating wallet client`,
199
205
  cause: err instanceof Error ? err : undefined,
200
206
  context: {
201
- MocksEncryptedInputSignerPkey,
207
+ MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY,
202
208
  },
203
209
  });
204
210
  }
@@ -258,8 +264,8 @@ export async function cofheMocksZkVerifySign(
258
264
  walletClient: WalletClient,
259
265
  zkvWalletClient: WalletClient | undefined
260
266
  ): Promise<VerifyResult[]> {
261
- // Use config.mocks.zkvWalletClient if provided, otherwise use connected walletClient
262
- const _walletClient = zkvWalletClient ?? walletClient;
267
+ // Use config._internal?.zkvWalletClient if provided, otherwise use a mock zk verifier signer
268
+ const _walletClient = zkvWalletClient ?? createMockZkVerifierSigner();
263
269
 
264
270
  // Call MockZkVerifier contract to calculate the ctHashes
265
271
  const encryptableItems = await calcCtHashes(items, account, securityZone, publicClient);