@cofhe/sdk 0.6.0 → 0.7.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 +49 -0
- package/acps/acp.ts +411 -0
- package/acps/index.ts +70 -0
- package/{permits → acps}/onchain-utils.ts +49 -24
- package/acps/sealing.ts +90 -0
- package/acps/signature.ts +89 -0
- package/acps/store.ts +172 -0
- package/acps/test/acp.test.ts +615 -0
- package/acps/test/localstorage.test.ts +105 -0
- package/acps/test/sealing.test.ts +77 -0
- package/acps/test/store.test.ts +88 -0
- package/acps/test/validation.test.ts +361 -0
- package/acps/test-utils.ts +32 -0
- package/acps/types.ts +252 -0
- package/acps/validation.ts +392 -0
- package/adapters/test/ethers5.test.ts +4 -1
- package/adapters/test/ethers6.test.ts +4 -1
- package/adapters/test/wagmi.test.ts +5 -2
- package/chains/chains/stagingCofhe.ts +21 -0
- package/chains/index.ts +3 -1
- package/chains/test/chains.test.ts +2 -1
- package/core/acps.ts +625 -0
- package/core/client.ts +136 -39
- package/core/clientTypes.ts +52 -41
- package/core/config.ts +66 -5
- package/core/debug.ts +72 -0
- package/core/decrypt/MockThresholdNetworkAbi.ts +20 -11
- package/core/decrypt/apiError.ts +104 -0
- package/core/decrypt/cofheMocksDecryptForTx.ts +11 -11
- package/core/decrypt/cofheMocksDecryptForView.ts +7 -7
- package/core/decrypt/decryptForTxBuilder.ts +102 -102
- package/core/decrypt/decryptForViewBuilder.ts +90 -90
- package/core/decrypt/submitRetry.ts +38 -30
- package/core/decrypt/tnDecryptV1.ts +5 -5
- package/core/decrypt/tnDecryptV2.ts +45 -34
- package/core/decrypt/tnSealOutputV1.ts +4 -3
- package/core/decrypt/tnSealOutputV2.ts +44 -31
- package/core/encrypt/cofheMocksZkVerifySign.ts +59 -74
- package/core/encrypt/encryptInputsBuilder.ts +86 -52
- package/core/encrypt/zkPackProveVerify.ts +25 -18
- package/core/error.ts +34 -6
- package/core/index.ts +14 -14
- package/core/test/acpDefaults.test.ts +52 -0
- package/core/test/acps.test.ts +596 -0
- package/core/test/apiError.test.ts +130 -0
- package/core/test/client.test.ts +22 -19
- package/core/test/config.test.ts +25 -5
- package/core/test/decrypt.test.ts +40 -35
- package/core/test/decryptBuilders.test.ts +68 -68
- package/core/test/decryptErrorCodes.test.ts +217 -0
- package/core/test/encryptInputsBuilder.test.ts +72 -41
- package/core/test/pollCallbacks.test.ts +89 -18
- package/core/test/stagingRedirect.ts +18 -0
- package/core/test/submitRetry.test.ts +182 -0
- package/core/types.ts +9 -69
- package/dist/acp-Wi6isVQI.d.cts +407 -0
- package/dist/acp-Wi6isVQI.d.ts +407 -0
- package/dist/acps.cjs +1081 -0
- package/dist/acps.d.cts +480 -0
- package/dist/acps.d.ts +480 -0
- package/dist/acps.js +2 -0
- package/dist/chains.cjs +14 -1
- package/dist/chains.d.cts +30 -1
- package/dist/chains.d.ts +30 -1
- package/dist/chains.js +1 -1
- package/dist/{chunk-PE5V5CCV.js → chunk-43USWPEH.js} +1000 -604
- package/dist/{chunk-MTRAXQXC.js → chunk-N6IDQRRU.js} +14 -2
- package/dist/chunk-Q7CBWGQX.js +1029 -0
- package/dist/{clientTypes-CyUvRRzA.d.ts → clientTypes-BN3nbzYM.d.ts} +342 -165
- package/dist/{clientTypes-BDy1qIBu.d.cts → clientTypes-CYZjFznO.d.cts} +342 -165
- package/dist/core.cjs +1431 -1026
- package/dist/core.d.cts +55 -10
- package/dist/core.d.ts +55 -10
- package/dist/core.js +3 -3
- package/dist/node.cjs +1356 -976
- package/dist/node.d.cts +2 -2
- package/dist/node.d.ts +2 -2
- package/dist/node.js +3 -3
- package/dist/web.cjs +1356 -976
- package/dist/web.d.cts +2 -2
- package/dist/web.d.ts +2 -2
- package/dist/web.js +3 -3
- package/node/test/inherited.test.ts +75 -65
- package/node/test/tfheinit.test.ts +23 -8
- package/package.json +6 -6
- package/web/test/client.web.test.ts +5 -1
- package/web/test/inherited.web.test.ts +75 -65
- package/web/test/tfheinit.web.test.ts +14 -5
- package/web/test/worker.config.web.test.ts +38 -23
- package/web/test/worker.output.web.test.ts +25 -24
- package/core/encrypt/encryptUtils.ts +0 -67
- package/core/permits.ts +0 -206
- package/core/test/permits.test.ts +0 -534
- package/dist/chunk-VB62WYPL.js +0 -978
- package/dist/permit-DnVMDT5h.d.cts +0 -376
- package/dist/permit-DnVMDT5h.d.ts +0 -376
- package/dist/permits.cjs +0 -1026
- package/dist/permits.d.cts +0 -353
- package/dist/permits.d.ts +0 -353
- package/dist/permits.js +0 -2
- package/permits/index.ts +0 -68
- package/permits/permit.ts +0 -385
- package/permits/sealing.ts +0 -131
- package/permits/signature.ts +0 -79
- package/permits/store.ts +0 -157
- package/permits/test/localstorage.test.ts +0 -113
- package/permits/test/permit.test.ts +0 -557
- package/permits/test/sealing.test.ts +0 -84
- package/permits/test/store.test.ts +0 -88
- package/permits/test/validation.test.ts +0 -361
- package/permits/test-utils.ts +0 -28
- package/permits/types.ts +0 -204
- package/permits/validation.ts +0 -327
- /package/{permits → acps}/utils.ts +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/* eslint-disable no-dupe-class-members */
|
|
2
2
|
import { hardhat } from '@/chains';
|
|
3
|
-
import { type
|
|
3
|
+
import { type ACP, ACPUtils } from '@/acps';
|
|
4
4
|
|
|
5
5
|
import { FheTypes, type UnsealedItem } from '../types.js';
|
|
6
6
|
import { getThresholdNetworkUrlOrThrow } from '../config.js';
|
|
7
7
|
import { CofheError, CofheErrorCode } from '../error.js';
|
|
8
|
-
import {
|
|
8
|
+
import { acps } from '../acps.js';
|
|
9
9
|
import { isValidUtype, convertViaUtype } from './decryptUtils.js';
|
|
10
10
|
import { BaseBuilder, type BaseBuilderParams } from '../baseBuilder.js';
|
|
11
11
|
import { cofheMocksDecryptForView } from './cofheMocksDecryptForView.js';
|
|
@@ -22,17 +22,17 @@ const DEFAULT_404_RETRY_TIMEOUT_MS = 10_000;
|
|
|
22
22
|
* await client.decryptForView(ctHash, utype)
|
|
23
23
|
* .setChainId(chainId)
|
|
24
24
|
* .setAccount(account)
|
|
25
|
-
* .
|
|
26
|
-
* // or .
|
|
25
|
+
* .withACP() // optional (active acp)
|
|
26
|
+
* // or .withACP(acpHash) / .withACP(acp)
|
|
27
27
|
* .execute()
|
|
28
28
|
*
|
|
29
29
|
* If chainId not set, uses client's chainId
|
|
30
30
|
* If account not set, uses client's account
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
* withACP() uses chainId + account to get the active acp.
|
|
32
|
+
* withACP(acpHash) fetches that acp using chainId + account.
|
|
33
|
+
* withACP(acp) uses the provided acp regardless of chainId/account.
|
|
34
34
|
*
|
|
35
|
-
* Note: decryptForView always requires
|
|
35
|
+
* Note: decryptForView always requires an ACP (no global-allowance mode).
|
|
36
36
|
*
|
|
37
37
|
* Returns the unsealed item.
|
|
38
38
|
*/
|
|
@@ -40,15 +40,15 @@ const DEFAULT_404_RETRY_TIMEOUT_MS = 10_000;
|
|
|
40
40
|
type DecryptForViewBuilderParams<U extends FheTypes> = BaseBuilderParams & {
|
|
41
41
|
ctHash: bigint | string;
|
|
42
42
|
utype: U;
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
acpHash?: string;
|
|
44
|
+
acp?: ACP;
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
export class DecryptForViewBuilder<U extends FheTypes> extends BaseBuilder {
|
|
48
48
|
private ctHash: bigint | string;
|
|
49
49
|
private utype: U;
|
|
50
|
-
private
|
|
51
|
-
private
|
|
50
|
+
private acpHash?: string;
|
|
51
|
+
private acp?: ACP;
|
|
52
52
|
private pollCallback?: DecryptPollCallbackFunction;
|
|
53
53
|
private retry404TimeoutMs = DEFAULT_404_RETRY_TIMEOUT_MS;
|
|
54
54
|
|
|
@@ -64,12 +64,12 @@ export class DecryptForViewBuilder<U extends FheTypes> extends BaseBuilder {
|
|
|
64
64
|
|
|
65
65
|
this.ctHash = params.ctHash;
|
|
66
66
|
this.utype = params.utype;
|
|
67
|
-
this.
|
|
68
|
-
this.
|
|
67
|
+
this.acpHash = params.acpHash;
|
|
68
|
+
this.acp = params.acp;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
|
-
* @param chainId - Chain to decrypt values from. Used to fetch the threshold network URL and use the correct
|
|
72
|
+
* @param chainId - Chain to decrypt values from. Used to fetch the threshold network URL and use the correct acp.
|
|
73
73
|
*
|
|
74
74
|
* If not provided, the chainId will be fetched from the connected publicClient.
|
|
75
75
|
*
|
|
@@ -92,7 +92,7 @@ export class DecryptForViewBuilder<U extends FheTypes> extends BaseBuilder {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
/**
|
|
95
|
-
* @param account - Account to decrypt values from. Used to fetch the correct
|
|
95
|
+
* @param account - Account to decrypt values from. Used to fetch the correct acp.
|
|
96
96
|
*
|
|
97
97
|
* If not provided, the account will be fetched from the connected walletClient.
|
|
98
98
|
*
|
|
@@ -135,77 +135,77 @@ export class DecryptForViewBuilder<U extends FheTypes> extends BaseBuilder {
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
/**
|
|
138
|
-
* Select "use
|
|
138
|
+
* Select "use acp" mode (optional).
|
|
139
139
|
*
|
|
140
|
-
* - `
|
|
141
|
-
* - `
|
|
142
|
-
* - `
|
|
140
|
+
* - `withACP(acp)` uses the provided acp.
|
|
141
|
+
* - `withACP(acpHash)` fetches that acp.
|
|
142
|
+
* - `withACP()` uses the active acp for the resolved `chainId + account`.
|
|
143
143
|
*/
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if (typeof
|
|
149
|
-
this.
|
|
150
|
-
this.
|
|
151
|
-
} else if (
|
|
152
|
-
// Explicitly choose "active
|
|
153
|
-
this.
|
|
154
|
-
this.
|
|
144
|
+
withACP(): DecryptForViewBuilder<U>;
|
|
145
|
+
withACP(acpHash: string): DecryptForViewBuilder<U>;
|
|
146
|
+
withACP(acp: ACP): DecryptForViewBuilder<U>;
|
|
147
|
+
withACP(acpOrACPHash?: ACP | string): DecryptForViewBuilder<U> {
|
|
148
|
+
if (typeof acpOrACPHash === 'string') {
|
|
149
|
+
this.acpHash = acpOrACPHash;
|
|
150
|
+
this.acp = undefined;
|
|
151
|
+
} else if (acpOrACPHash === undefined) {
|
|
152
|
+
// Explicitly choose "active acp" resolution at execute()
|
|
153
|
+
this.acpHash = undefined;
|
|
154
|
+
this.acp = undefined;
|
|
155
155
|
} else {
|
|
156
|
-
//
|
|
157
|
-
this.
|
|
158
|
-
this.
|
|
156
|
+
// ACP object
|
|
157
|
+
this.acp = acpOrACPHash;
|
|
158
|
+
this.acpHash = undefined;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
return this;
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
/**
|
|
165
|
-
* @param
|
|
165
|
+
* @param acpHash - ACP hash to decrypt values from. Used to fetch the correct acp.
|
|
166
166
|
*
|
|
167
|
-
* If not provided, the active
|
|
168
|
-
* If `
|
|
167
|
+
* If not provided, the active acp for the chainId and account will be used.
|
|
168
|
+
* If `setACP()` is called, it will be used regardless of chainId, account, or acpHash.
|
|
169
169
|
*
|
|
170
170
|
* Example:
|
|
171
171
|
* ```typescript
|
|
172
172
|
* const unsealed = await client.decryptForView(ctHash, utype)
|
|
173
|
-
* .
|
|
173
|
+
* .setACPHash('0x1234567890123456789012345678901234567890')
|
|
174
174
|
* .execute();
|
|
175
175
|
* ```
|
|
176
176
|
*
|
|
177
177
|
* @returns The chainable DecryptForViewBuilder instance.
|
|
178
178
|
*/
|
|
179
|
-
/** @deprecated Use `
|
|
180
|
-
|
|
181
|
-
return this.
|
|
179
|
+
/** @deprecated Use `withACP(acpHash)` instead. */
|
|
180
|
+
setACPHash(acpHash: string): DecryptForViewBuilder<U> {
|
|
181
|
+
return this.withACP(acpHash);
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
-
|
|
185
|
-
return this.
|
|
184
|
+
getACPHash(): string | undefined {
|
|
185
|
+
return this.acpHash;
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
/**
|
|
189
|
-
* @param
|
|
189
|
+
* @param acp - ACP to decrypt values with. If provided, it will be used regardless of chainId, account, or acpHash.
|
|
190
190
|
*
|
|
191
|
-
* If not provided, the
|
|
191
|
+
* If not provided, the acp will be determined by chainId, account, and acpHash.
|
|
192
192
|
*
|
|
193
193
|
* Example:
|
|
194
194
|
* ```typescript
|
|
195
195
|
* const unsealed = await client.decryptForView(ctHash, utype)
|
|
196
|
-
* .
|
|
196
|
+
* .setACP(acp)
|
|
197
197
|
* .execute();
|
|
198
198
|
* ```
|
|
199
199
|
*
|
|
200
200
|
* @returns The chainable DecryptForViewBuilder instance.
|
|
201
201
|
*/
|
|
202
|
-
/** @deprecated Use `
|
|
203
|
-
|
|
204
|
-
return this.
|
|
202
|
+
/** @deprecated Use `withACP(acp)` instead. */
|
|
203
|
+
setACP(acp: ACP): DecryptForViewBuilder<U> {
|
|
204
|
+
return this.withACP(acp);
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
-
|
|
208
|
-
return this.
|
|
207
|
+
getACP(): ACP | undefined {
|
|
208
|
+
return this.acp;
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
private async getThresholdNetworkUrl(): Promise<string> {
|
|
@@ -224,50 +224,50 @@ export class DecryptForViewBuilder<U extends FheTypes> extends BaseBuilder {
|
|
|
224
224
|
});
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
private async
|
|
228
|
-
if (this.
|
|
227
|
+
private async getResolvedACP(): Promise<ACP> {
|
|
228
|
+
if (this.acp) return this.acp;
|
|
229
229
|
|
|
230
230
|
this.assertChainId();
|
|
231
231
|
this.assertAccount();
|
|
232
232
|
|
|
233
|
-
// Fetch with
|
|
234
|
-
if (this.
|
|
235
|
-
const
|
|
236
|
-
if (!
|
|
233
|
+
// Fetch with acp hash
|
|
234
|
+
if (this.acpHash) {
|
|
235
|
+
const acp = await acps.getACP(this.chainId, this.account, this.acpHash);
|
|
236
|
+
if (!acp) {
|
|
237
237
|
throw new CofheError({
|
|
238
|
-
code: CofheErrorCode.
|
|
239
|
-
message: `
|
|
240
|
-
hint: 'Ensure the
|
|
238
|
+
code: CofheErrorCode.ACPNotFound,
|
|
239
|
+
message: `ACP with hash <${this.acpHash}> not found for account <${this.account}> and chainId <${this.chainId}>`,
|
|
240
|
+
hint: 'Ensure the acp exists and is valid.',
|
|
241
241
|
context: {
|
|
242
242
|
chainId: this.chainId,
|
|
243
243
|
account: this.account,
|
|
244
|
-
|
|
244
|
+
acpHash: this.acpHash,
|
|
245
245
|
},
|
|
246
246
|
});
|
|
247
247
|
}
|
|
248
|
-
return
|
|
248
|
+
return acp;
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
-
// Fetch with active
|
|
252
|
-
const
|
|
253
|
-
if (!
|
|
251
|
+
// Fetch with active acp
|
|
252
|
+
const acp = await acps.getActiveACP(this.chainId, this.account);
|
|
253
|
+
if (!acp) {
|
|
254
254
|
throw new CofheError({
|
|
255
|
-
code: CofheErrorCode.
|
|
256
|
-
message: `Active
|
|
257
|
-
hint: 'Ensure
|
|
255
|
+
code: CofheErrorCode.ACPNotFound,
|
|
256
|
+
message: `Active acp not found for chainId <${this.chainId}> and account <${this.account}>`,
|
|
257
|
+
hint: 'Ensure an ACP exists for this account on this chain.',
|
|
258
258
|
context: {
|
|
259
259
|
chainId: this.chainId,
|
|
260
260
|
account: this.account,
|
|
261
261
|
},
|
|
262
262
|
});
|
|
263
263
|
}
|
|
264
|
-
return
|
|
264
|
+
return acp;
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
/**
|
|
268
268
|
* On hardhat, interact with MockZkVerifier contract instead of CoFHE
|
|
269
269
|
*/
|
|
270
|
-
private async mocksSealOutput(
|
|
270
|
+
private async mocksSealOutput(acp: ACP): Promise<bigint> {
|
|
271
271
|
this.assertPublicClient();
|
|
272
272
|
|
|
273
273
|
// Configurable delay before decrypting the output to simulate the CoFHE decrypt processing time
|
|
@@ -276,38 +276,38 @@ export class DecryptForViewBuilder<U extends FheTypes> extends BaseBuilder {
|
|
|
276
276
|
const mocksDecryptDelay = this.config.mocks.decryptDelay;
|
|
277
277
|
if (mocksDecryptDelay > 0) await sleep(mocksDecryptDelay);
|
|
278
278
|
|
|
279
|
-
return cofheMocksDecryptForView(this.ctHash, this.utype,
|
|
279
|
+
return cofheMocksDecryptForView(this.ctHash, this.utype, acp, this.publicClient);
|
|
280
280
|
}
|
|
281
281
|
|
|
282
282
|
/**
|
|
283
283
|
* In the production context, perform a true decryption with the CoFHE coprocessor.
|
|
284
284
|
*/
|
|
285
|
-
private async productionSealOutput(
|
|
285
|
+
private async productionSealOutput(acp: ACP): Promise<bigint> {
|
|
286
286
|
this.assertChainId();
|
|
287
287
|
this.assertPublicClient();
|
|
288
288
|
|
|
289
289
|
const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
|
|
290
|
-
const
|
|
290
|
+
const wireAcp = ACPUtils.getPublic(acp, true);
|
|
291
291
|
// const sealed = await tnSealOutputV1(this.ctHash, this.chainId, permission, thresholdNetworkUrl);
|
|
292
292
|
const sealed = await tnSealOutputV2({
|
|
293
293
|
ctHash: this.ctHash,
|
|
294
294
|
chainId: this.chainId,
|
|
295
|
-
|
|
295
|
+
acp: wireAcp,
|
|
296
296
|
thresholdNetworkUrl,
|
|
297
297
|
retry404TimeoutMs: this.retry404TimeoutMs,
|
|
298
298
|
onPoll: this.pollCallback,
|
|
299
299
|
});
|
|
300
|
-
return
|
|
300
|
+
return ACPUtils.unseal(acp, sealed);
|
|
301
301
|
}
|
|
302
302
|
|
|
303
303
|
/**
|
|
304
304
|
* Final step of the decryption process. MUST BE CALLED LAST IN THE CHAIN.
|
|
305
305
|
*
|
|
306
306
|
* This will:
|
|
307
|
-
* - Use
|
|
308
|
-
* - Check
|
|
309
|
-
* - Call CoFHE `/sealoutput` with the
|
|
310
|
-
* - Unseal the sealed item with the
|
|
307
|
+
* - Use an ACP based on provided acp OR chainId + account + acpHash
|
|
308
|
+
* - Check acp validity
|
|
309
|
+
* - Call CoFHE `/sealoutput` with the acp, which returns a sealed (encrypted) item
|
|
310
|
+
* - Unseal the sealed item with the acp
|
|
311
311
|
* - Return the unsealed item
|
|
312
312
|
*
|
|
313
313
|
* Example:
|
|
@@ -315,7 +315,7 @@ export class DecryptForViewBuilder<U extends FheTypes> extends BaseBuilder {
|
|
|
315
315
|
* const unsealed = await client.decryptForView(ctHash, utype)
|
|
316
316
|
* .setChainId(11155111) // optional
|
|
317
317
|
* .setAccount('0x123...890') // optional
|
|
318
|
-
* .
|
|
318
|
+
* .withACP() // optional
|
|
319
319
|
* .execute(); // execute
|
|
320
320
|
* ```
|
|
321
321
|
*
|
|
@@ -325,25 +325,25 @@ export class DecryptForViewBuilder<U extends FheTypes> extends BaseBuilder {
|
|
|
325
325
|
// Ensure utype is valid
|
|
326
326
|
this.validateUtypeOrThrow();
|
|
327
327
|
|
|
328
|
-
// Resolve
|
|
329
|
-
const
|
|
328
|
+
// Resolve acp
|
|
329
|
+
const acp = await this.getResolvedACP();
|
|
330
330
|
|
|
331
|
-
// Ensure
|
|
332
|
-
|
|
331
|
+
// Ensure acp validity
|
|
332
|
+
ACPUtils.validate(acp);
|
|
333
333
|
|
|
334
|
-
// Extract chainId from signed
|
|
334
|
+
// Extract chainId from signed acp
|
|
335
335
|
// Use this chainId to fetch the threshold network URL since this.chainId may be undefined
|
|
336
|
-
const chainId =
|
|
336
|
+
const chainId = acp._signedDomain!.chainId;
|
|
337
337
|
|
|
338
|
-
// Check
|
|
339
|
-
// TODO:
|
|
338
|
+
// Check acp validity on-chain
|
|
339
|
+
// TODO: ACPUtils.validateOnChain(acp, this.publicClient);
|
|
340
340
|
|
|
341
341
|
let unsealed: bigint;
|
|
342
342
|
|
|
343
343
|
if (chainId === hardhat.id) {
|
|
344
|
-
unsealed = await this.mocksSealOutput(
|
|
344
|
+
unsealed = await this.mocksSealOutput(acp);
|
|
345
345
|
} else {
|
|
346
|
-
unsealed = await this.productionSealOutput(
|
|
346
|
+
unsealed = await this.productionSealOutput(acp);
|
|
347
347
|
}
|
|
348
348
|
|
|
349
349
|
return convertViaUtype(this.utype, unsealed);
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { CofheError, CofheErrorCode } from '../error.js';
|
|
2
|
+
import { mapApiErrorCodeToCofheErrorCode, parseApiErrorResponseBody } from './apiError.js';
|
|
2
3
|
|
|
3
4
|
export const DEFAULT_404_RETRY_TIMEOUT_MS = 10_000;
|
|
4
5
|
|
|
5
6
|
export type SubmitRetryableStatus = 204 | 404;
|
|
6
7
|
|
|
7
8
|
export type SubmitResponseClassification =
|
|
8
|
-
| { kind: 'retryable'; status:
|
|
9
|
+
| { kind: 'retryable'; status: 204 }
|
|
10
|
+
| { kind: 'retryable'; status: 404; apiErrorMessage: string }
|
|
9
11
|
| { kind: 'parse-json' }
|
|
10
|
-
| {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
| {
|
|
13
|
+
kind: 'fatal-http';
|
|
14
|
+
status: number;
|
|
15
|
+
cofheErrorCode: CofheErrorCode;
|
|
16
|
+
apiErrorCode?: string;
|
|
17
|
+
errorMessage: string;
|
|
18
|
+
};
|
|
15
19
|
|
|
16
20
|
export function normalize404RetryTimeoutMs(params: {
|
|
17
21
|
timeoutMs: number | undefined;
|
|
@@ -35,40 +39,39 @@ export function normalize404RetryTimeoutMs(params: {
|
|
|
35
39
|
return timeoutMs;
|
|
36
40
|
}
|
|
37
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Classifies a submit (POST) response into: bodyless-retryable 204, any 404 (still-indexing /
|
|
44
|
+
* not-found — retryable under the 404 retry budget), a 200 to be JSON-parsed, or a fatal error
|
|
45
|
+
* carrying the mapped CofheErrorCode.
|
|
46
|
+
*/
|
|
38
47
|
export async function classifySubmitResponse(params: {
|
|
39
48
|
response: Response;
|
|
40
|
-
|
|
49
|
+
fallbackErrorCode: CofheErrorCode;
|
|
41
50
|
}): Promise<SubmitResponseClassification> {
|
|
42
|
-
const { response,
|
|
51
|
+
const { response, fallbackErrorCode } = params;
|
|
43
52
|
|
|
44
|
-
|
|
45
|
-
|
|
53
|
+
// 204 is documented bodyless — never touch response.json() for it.
|
|
54
|
+
if (response.status === 204) {
|
|
55
|
+
return { kind: 'retryable', status: 204 };
|
|
46
56
|
}
|
|
47
57
|
|
|
48
58
|
if (response.ok) {
|
|
49
59
|
return { kind: 'parse-json' };
|
|
50
60
|
}
|
|
51
61
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (typeof maybeErrorMessage === 'string' && maybeErrorMessage.length > 0) {
|
|
57
|
-
errorMessage = maybeErrorMessage;
|
|
58
|
-
} else if (errorBody && typeof errorBody === 'object') {
|
|
59
|
-
const defaultMessage = (errorBody as Record<string, unknown>).error_message;
|
|
60
|
-
const fallbackMessage = (errorBody as Record<string, unknown>).message;
|
|
61
|
-
if (typeof defaultMessage === 'string' && defaultMessage.length > 0) {
|
|
62
|
-
errorMessage = defaultMessage;
|
|
63
|
-
} else if (typeof fallbackMessage === 'string' && fallbackMessage.length > 0) {
|
|
64
|
-
errorMessage = fallbackMessage;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
} catch {
|
|
68
|
-
errorMessage = response.statusText || errorMessage;
|
|
62
|
+
const { apiErrorCode, errorMessage } = await parseApiErrorResponseBody(response);
|
|
63
|
+
|
|
64
|
+
if (response.status === 404) {
|
|
65
|
+
return { kind: 'retryable', status: 404, apiErrorMessage: errorMessage };
|
|
69
66
|
}
|
|
70
67
|
|
|
71
|
-
return {
|
|
68
|
+
return {
|
|
69
|
+
kind: 'fatal-http',
|
|
70
|
+
status: response.status,
|
|
71
|
+
cofheErrorCode: mapApiErrorCodeToCofheErrorCode(apiErrorCode, fallbackErrorCode),
|
|
72
|
+
apiErrorCode,
|
|
73
|
+
errorMessage,
|
|
74
|
+
};
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
export function throwIfSubmitRetryTimedOut(params: {
|
|
@@ -81,6 +84,7 @@ export function throwIfSubmitRetryTimedOut(params: {
|
|
|
81
84
|
thresholdNetworkUrl: string;
|
|
82
85
|
body: unknown;
|
|
83
86
|
attemptIndex: number;
|
|
87
|
+
lastKnownErrorMessage?: string;
|
|
84
88
|
}): void {
|
|
85
89
|
const {
|
|
86
90
|
operationLabel,
|
|
@@ -92,12 +96,16 @@ export function throwIfSubmitRetryTimedOut(params: {
|
|
|
92
96
|
thresholdNetworkUrl,
|
|
93
97
|
body,
|
|
94
98
|
attemptIndex,
|
|
99
|
+
lastKnownErrorMessage,
|
|
95
100
|
} = params;
|
|
96
101
|
|
|
97
102
|
if (status === 404 && elapsedMs > retry404TimeoutMs) {
|
|
98
103
|
throw new CofheError({
|
|
99
|
-
code:
|
|
100
|
-
|
|
104
|
+
code: CofheErrorCode.CtNotFound,
|
|
105
|
+
apiErrorCode: 'ct_not_found',
|
|
106
|
+
message:
|
|
107
|
+
`${operationLabel} ciphertext not found after retrying for ${retry404TimeoutMs}ms` +
|
|
108
|
+
(lastKnownErrorMessage ? `: ${lastKnownErrorMessage}` : ''),
|
|
101
109
|
hint: 'The ciphertext may not be indexed yet. Increase set404RetryTimeout(...) if the backend is slow to index ciphertexts.',
|
|
102
110
|
context: {
|
|
103
111
|
thresholdNetworkUrl,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ACPPublic } from '@/acps';
|
|
2
2
|
|
|
3
3
|
import { CofheError, CofheErrorCode } from '../error';
|
|
4
4
|
import { normalizeTnSignature, parseDecryptedBytesToBigInt } from './tnDecryptUtils';
|
|
@@ -68,20 +68,20 @@ function assertTnDecryptResponseV1(value: unknown): TnDecryptResponseV1 {
|
|
|
68
68
|
export async function tnDecryptV1(
|
|
69
69
|
ctHash: bigint | string,
|
|
70
70
|
chainId: number,
|
|
71
|
-
|
|
71
|
+
acp: ACPPublic | null,
|
|
72
72
|
thresholdNetworkUrl: string
|
|
73
73
|
): Promise<{ decryptedValue: bigint; signature: `0x${string}` }> {
|
|
74
74
|
const body: {
|
|
75
75
|
ct_tempkey: string;
|
|
76
76
|
host_chain_id: number;
|
|
77
|
-
|
|
77
|
+
acp?: ACPPublic;
|
|
78
78
|
} = {
|
|
79
79
|
ct_tempkey: BigInt(ctHash).toString(16).padStart(64, '0'),
|
|
80
80
|
host_chain_id: chainId,
|
|
81
81
|
};
|
|
82
82
|
|
|
83
|
-
if (
|
|
84
|
-
body.
|
|
83
|
+
if (acp) {
|
|
84
|
+
body.acp = acp;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
let response: Response;
|