@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.
- package/CHANGELOG.md +14 -0
- package/adapters/ethers6.ts +28 -28
- package/adapters/hardhat.ts +0 -1
- package/adapters/index.test.ts +14 -19
- package/adapters/smartWallet.ts +81 -73
- package/adapters/test-utils.ts +45 -45
- package/adapters/types.ts +3 -3
- package/chains/chains/localcofhe.ts +14 -0
- package/chains/chains.test.ts +2 -1
- package/chains/index.ts +3 -1
- package/core/baseBuilder.ts +30 -49
- package/core/client.test.ts +94 -77
- package/core/client.ts +133 -149
- package/core/clientTypes.ts +108 -0
- package/core/config.test.ts +22 -11
- package/core/config.ts +16 -9
- package/core/decrypt/decryptHandleBuilder.ts +51 -45
- package/core/decrypt/{tnSealOutput.ts → tnSealOutputV1.ts} +1 -1
- package/core/decrypt/tnSealOutputV2.ts +298 -0
- package/core/encrypt/cofheMocksZkVerifySign.ts +16 -10
- package/core/encrypt/encryptInputsBuilder.test.ts +132 -116
- package/core/encrypt/encryptInputsBuilder.ts +159 -111
- package/core/encrypt/encryptUtils.ts +6 -3
- package/core/encrypt/zkPackProveVerify.ts +70 -8
- package/core/error.ts +0 -2
- package/core/fetchKeys.test.ts +1 -18
- package/core/fetchKeys.ts +0 -26
- package/core/index.ts +29 -17
- package/core/keyStore.ts +65 -38
- package/core/permits.test.ts +253 -1
- package/core/permits.ts +80 -16
- package/core/types.ts +198 -152
- package/core/utils.ts +43 -1
- package/dist/adapters.d.cts +38 -20
- package/dist/adapters.d.ts +38 -20
- package/dist/chains.cjs +14 -1
- package/dist/chains.d.cts +23 -1
- package/dist/chains.d.ts +23 -1
- package/dist/chains.js +1 -1
- package/dist/{chunk-LU7BMUUT.js → chunk-UGBVZNRT.js} +39 -25
- package/dist/{chunk-GZCQQYVI.js → chunk-WEAZ25JO.js} +14 -2
- package/dist/{chunk-KFGPTJ6X.js → chunk-WGCRJCBR.js} +1920 -1692
- package/dist/{types-bB7wLj0q.d.cts → clientTypes-5_1nwtUe.d.cts} +308 -347
- package/dist/{types-PhwGgQvs.d.ts → clientTypes-Es7fyi65.d.ts} +308 -347
- package/dist/core.cjs +2872 -2632
- package/dist/core.d.cts +101 -6
- package/dist/core.d.ts +101 -6
- package/dist/core.js +3 -3
- package/dist/node.cjs +2716 -2520
- package/dist/node.d.cts +3 -3
- package/dist/node.d.ts +3 -3
- package/dist/node.js +4 -3
- package/dist/{permit-S9CnI6MF.d.cts → permit-fUSe6KKq.d.cts} +31 -15
- package/dist/{permit-S9CnI6MF.d.ts → permit-fUSe6KKq.d.ts} +31 -15
- package/dist/permits.cjs +39 -24
- package/dist/permits.d.cts +137 -148
- package/dist/permits.d.ts +137 -148
- package/dist/permits.js +1 -1
- package/dist/web.cjs +2929 -2518
- package/dist/web.d.cts +21 -5
- package/dist/web.d.ts +21 -5
- package/dist/web.js +185 -9
- package/dist/zkProve.worker.cjs +93 -0
- package/dist/zkProve.worker.d.cts +2 -0
- package/dist/zkProve.worker.d.ts +2 -0
- package/dist/zkProve.worker.js +91 -0
- package/node/client.test.ts +20 -25
- package/node/encryptInputs.test.ts +18 -38
- package/node/index.ts +1 -0
- package/package.json +14 -14
- package/permits/index.ts +1 -0
- package/permits/localstorage.test.ts +0 -1
- package/permits/permit.test.ts +25 -22
- package/permits/permit.ts +30 -21
- package/permits/sealing.test.ts +3 -3
- package/permits/sealing.ts +2 -2
- package/permits/store.ts +5 -7
- package/permits/test-utils.ts +1 -1
- package/permits/types.ts +17 -0
- package/permits/utils.ts +0 -1
- package/permits/validation.ts +24 -4
- package/web/client.web.test.ts +20 -25
- package/web/config.web.test.ts +0 -2
- package/web/encryptInputs.web.test.ts +31 -54
- package/web/index.ts +65 -1
- package/web/storage.ts +19 -5
- package/web/worker.builder.web.test.ts +148 -0
- package/web/worker.config.web.test.ts +329 -0
- package/web/worker.output.web.test.ts +84 -0
- package/web/workerManager.test.ts +80 -0
- package/web/workerManager.ts +214 -0
- package/web/workerManager.web.test.ts +114 -0
- package/web/zkProve.worker.ts +133 -0
- package/core/result.test.ts +0 -180
- package/core/result.ts +0 -67
- 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
|
-
|
|
21
|
-
|
|
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 =
|
|
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
|
-
|
|
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.
|
|
262
|
-
const _walletClient = zkvWalletClient ??
|
|
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);
|