@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/dist/core.cjs
CHANGED
|
@@ -9,25 +9,9 @@ var middleware = require('zustand/middleware');
|
|
|
9
9
|
var immer = require('immer');
|
|
10
10
|
var nacl = require('tweetnacl');
|
|
11
11
|
|
|
12
|
-
function
|
|
13
|
-
if (e && e.__esModule) return e;
|
|
14
|
-
var n = Object.create(null);
|
|
15
|
-
if (e) {
|
|
16
|
-
Object.keys(e).forEach(function (k) {
|
|
17
|
-
if (k !== 'default') {
|
|
18
|
-
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
19
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
20
|
-
enumerable: true,
|
|
21
|
-
get: function () { return e[k]; }
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
n.default = e;
|
|
27
|
-
return Object.freeze(n);
|
|
28
|
-
}
|
|
12
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
29
13
|
|
|
30
|
-
var
|
|
14
|
+
var nacl__default = /*#__PURE__*/_interopDefault(nacl);
|
|
31
15
|
|
|
32
16
|
// core/client.ts
|
|
33
17
|
|
|
@@ -166,6 +150,18 @@ var bigintSafeJsonStringify = (value) => {
|
|
|
166
150
|
};
|
|
167
151
|
var isCofheError = (error) => error instanceof CofheError;
|
|
168
152
|
|
|
153
|
+
// core/consts.ts
|
|
154
|
+
var TASK_MANAGER_ADDRESS = "0xeA30c4B8b44078Bbf8a6ef5b9f1eC1626C7848D9";
|
|
155
|
+
var MOCKS_ZK_VERIFIER_ADDRESS = "0x0000000000000000000000000000000000005001";
|
|
156
|
+
var MOCKS_THRESHOLD_NETWORK_ADDRESS = "0x0000000000000000000000000000000000005002";
|
|
157
|
+
var TEST_BED_ADDRESS = "0x0000000000000000000000000000000000005003";
|
|
158
|
+
var MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY = "0x6C8D7F768A6BB4AAFE85E8A2F5A9680355239C7E14646ED62B044E39DE154512";
|
|
159
|
+
var MOCKS_ZK_VERIFIER_SIGNER_ADDRESS = "0x6E12D8C87503D4287c294f2Fdef96ACd9DFf6bd2";
|
|
160
|
+
var MOCKS_DECRYPT_RESULT_SIGNER_PRIVATE_KEY = "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
|
|
161
|
+
var TFHE_RS_ZK_MAX_BITS = 2048;
|
|
162
|
+
var TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT = BigInt(1 << 30);
|
|
163
|
+
var TFHE_RS_KEY_VERSION = 2;
|
|
164
|
+
|
|
169
165
|
// core/types.ts
|
|
170
166
|
var FheTypes = /* @__PURE__ */ ((FheTypes2) => {
|
|
171
167
|
FheTypes2[FheTypes2["Bool"] = 0] = "Bool";
|
|
@@ -395,7 +391,6 @@ var MAX_UINT32 = 4294967295n;
|
|
|
395
391
|
var MAX_UINT64 = 18446744073709551615n;
|
|
396
392
|
var MAX_UINT128 = 340282366920938463463374607431768211455n;
|
|
397
393
|
var MAX_UINT160 = 1461501637330902918203684832716283019655932542975n;
|
|
398
|
-
var MAX_ENCRYPTABLE_BITS = 2048;
|
|
399
394
|
var zkPack = (items, builder) => {
|
|
400
395
|
let totalBits = 0;
|
|
401
396
|
for (const item of items) {
|
|
@@ -459,14 +454,14 @@ var zkPack = (items, builder) => {
|
|
|
459
454
|
}
|
|
460
455
|
}
|
|
461
456
|
}
|
|
462
|
-
if (totalBits >
|
|
457
|
+
if (totalBits > TFHE_RS_ZK_MAX_BITS) {
|
|
463
458
|
throw new CofheError({
|
|
464
459
|
code: "ZK_PACK_FAILED" /* ZkPackFailed */,
|
|
465
|
-
message: `Total bits ${totalBits} exceeds ${
|
|
466
|
-
hint: `Ensure that the total bits of the items to encrypt does not exceed ${
|
|
460
|
+
message: `Total bits ${totalBits} exceeds ${TFHE_RS_ZK_MAX_BITS}`,
|
|
461
|
+
hint: `Ensure that the total bits of the items to encrypt does not exceed ${TFHE_RS_ZK_MAX_BITS}`,
|
|
467
462
|
context: {
|
|
468
463
|
totalBits,
|
|
469
|
-
maxBits:
|
|
464
|
+
maxBits: TFHE_RS_ZK_MAX_BITS,
|
|
470
465
|
items
|
|
471
466
|
}
|
|
472
467
|
});
|
|
@@ -485,7 +480,7 @@ var zkProve = async (builder, crs, metadata) => {
|
|
|
485
480
|
1
|
|
486
481
|
// ZkComputeLoad.Verify
|
|
487
482
|
);
|
|
488
|
-
resolve(compactList.
|
|
483
|
+
resolve(compactList.safe_serialize(TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT));
|
|
489
484
|
}, 0);
|
|
490
485
|
});
|
|
491
486
|
};
|
|
@@ -661,17 +656,6 @@ var MockZkVerifierAbi = [
|
|
|
661
656
|
},
|
|
662
657
|
{ type: "error", name: "InvalidInputs", inputs: [] }
|
|
663
658
|
];
|
|
664
|
-
|
|
665
|
-
// core/consts.ts
|
|
666
|
-
var TASK_MANAGER_ADDRESS = "0xeA30c4B8b44078Bbf8a6ef5b9f1eC1626C7848D9";
|
|
667
|
-
var MOCKS_ZK_VERIFIER_ADDRESS = "0x0000000000000000000000000000000000005001";
|
|
668
|
-
var MOCKS_THRESHOLD_NETWORK_ADDRESS = "0x0000000000000000000000000000000000005002";
|
|
669
|
-
var TEST_BED_ADDRESS = "0x0000000000000000000000000000000000005003";
|
|
670
|
-
var MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY = "0x6C8D7F768A6BB4AAFE85E8A2F5A9680355239C7E14646ED62B044E39DE154512";
|
|
671
|
-
var MOCKS_ZK_VERIFIER_SIGNER_ADDRESS = "0x6E12D8C87503D4287c294f2Fdef96ACd9DFf6bd2";
|
|
672
|
-
var MOCKS_DECRYPT_RESULT_SIGNER_PRIVATE_KEY = "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
|
|
673
|
-
|
|
674
|
-
// core/encrypt/cofheMocksZkVerifySign.ts
|
|
675
659
|
function createMockZkVerifierSigner() {
|
|
676
660
|
return viem.createWalletClient({
|
|
677
661
|
chain: chains.hardhat,
|
|
@@ -713,14 +697,14 @@ async function cofheMocksCheckEncryptableBits(items) {
|
|
|
713
697
|
}
|
|
714
698
|
}
|
|
715
699
|
}
|
|
716
|
-
if (totalBits >
|
|
700
|
+
if (totalBits > TFHE_RS_ZK_MAX_BITS) {
|
|
717
701
|
throw new CofheError({
|
|
718
702
|
code: "ZK_PACK_FAILED" /* ZkPackFailed */,
|
|
719
|
-
message: `Total bits ${totalBits} exceeds ${
|
|
720
|
-
hint: `Ensure that the total bits of the items to encrypt does not exceed ${
|
|
703
|
+
message: `Total bits ${totalBits} exceeds ${TFHE_RS_ZK_MAX_BITS}`,
|
|
704
|
+
hint: `Ensure that the total bits of the items to encrypt does not exceed ${TFHE_RS_ZK_MAX_BITS}`,
|
|
721
705
|
context: {
|
|
722
706
|
totalBits,
|
|
723
|
-
maxBits:
|
|
707
|
+
maxBits: TFHE_RS_ZK_MAX_BITS,
|
|
724
708
|
items
|
|
725
709
|
}
|
|
726
710
|
});
|
|
@@ -994,6 +978,7 @@ function getThresholdNetworkUrlOrThrow(config, chainId) {
|
|
|
994
978
|
}
|
|
995
979
|
return url;
|
|
996
980
|
}
|
|
981
|
+
var KEYSTORE_NAME = `cofhesdk-keys-v${TFHE_RS_KEY_VERSION}`;
|
|
997
982
|
function isValidPersistedState(state) {
|
|
998
983
|
if (state && typeof state === "object") {
|
|
999
984
|
if ("fhe" in state && "crs" in state) {
|
|
@@ -1048,7 +1033,7 @@ function createKeysStore(storage) {
|
|
|
1048
1033
|
};
|
|
1049
1034
|
const clearKeysStorage = async () => {
|
|
1050
1035
|
if (storage) {
|
|
1051
|
-
await storage.removeItem(
|
|
1036
|
+
await storage.removeItem(KEYSTORE_NAME);
|
|
1052
1037
|
}
|
|
1053
1038
|
};
|
|
1054
1039
|
const rehydrateKeysStore = async () => {
|
|
@@ -1078,7 +1063,7 @@ function createStoreWithPersit(storage) {
|
|
|
1078
1063
|
if (_error)
|
|
1079
1064
|
throw new Error(`onRehydrateStorage: Error rehydrating keys store: ${_error}`);
|
|
1080
1065
|
},
|
|
1081
|
-
name:
|
|
1066
|
+
name: KEYSTORE_NAME,
|
|
1082
1067
|
storage: middleware.createJSONStorage(() => storage),
|
|
1083
1068
|
merge: (persistedState, currentState) => {
|
|
1084
1069
|
const persisted = isValidPersistedState(persistedState) ? persistedState : DEFAULT_KEYS_STORE;
|
|
@@ -1803,7 +1788,7 @@ var SealingKey = class _SealingKey {
|
|
|
1803
1788
|
const ephemPublicKey = parsedData.public_key instanceof Uint8Array ? parsedData.public_key : new Uint8Array(parsedData.public_key);
|
|
1804
1789
|
const dataToDecrypt = parsedData.data instanceof Uint8Array ? parsedData.data : new Uint8Array(parsedData.data);
|
|
1805
1790
|
const privateKeyBytes = fromHexString(this.privateKey);
|
|
1806
|
-
const decryptedMessage =
|
|
1791
|
+
const decryptedMessage = nacl__default.default.box.open(dataToDecrypt, nonce, ephemPublicKey, privateKeyBytes);
|
|
1807
1792
|
if (!decryptedMessage) {
|
|
1808
1793
|
throw new Error("Failed to decrypt message");
|
|
1809
1794
|
}
|
|
@@ -1836,9 +1821,9 @@ var SealingKey = class _SealingKey {
|
|
|
1836
1821
|
static seal = (value, publicKey) => {
|
|
1837
1822
|
isString(publicKey);
|
|
1838
1823
|
isBigIntOrNumber(value);
|
|
1839
|
-
const ephemeralKeyPair =
|
|
1840
|
-
const nonce =
|
|
1841
|
-
const encryptedMessage =
|
|
1824
|
+
const ephemeralKeyPair = nacl__default.default.box.keyPair();
|
|
1825
|
+
const nonce = nacl__default.default.randomBytes(nacl__default.default.box.nonceLength);
|
|
1826
|
+
const encryptedMessage = nacl__default.default.box(toBeArray(value), nonce, fromHexString(publicKey), ephemeralKeyPair.secretKey);
|
|
1842
1827
|
return {
|
|
1843
1828
|
data: encryptedMessage,
|
|
1844
1829
|
public_key: ephemeralKeyPair.publicKey,
|
|
@@ -1847,7 +1832,7 @@ var SealingKey = class _SealingKey {
|
|
|
1847
1832
|
};
|
|
1848
1833
|
};
|
|
1849
1834
|
var GenerateSealingKey = () => {
|
|
1850
|
-
const sodiumKeypair =
|
|
1835
|
+
const sodiumKeypair = nacl__default.default.box.keyPair();
|
|
1851
1836
|
return new SealingKey(toHexString2(sodiumKeypair.secretKey), toHexString2(sodiumKeypair.publicKey));
|
|
1852
1837
|
};
|
|
1853
1838
|
var SerializedSealingPair = zod.z.object({
|
|
@@ -2005,9 +1990,9 @@ var ValidationUtils = {
|
|
|
2005
1990
|
return false;
|
|
2006
1991
|
},
|
|
2007
1992
|
/**
|
|
2008
|
-
*
|
|
1993
|
+
* Checks that a permit is signed and not expired.
|
|
2009
1994
|
*/
|
|
2010
|
-
|
|
1995
|
+
isSignedAndNotExpired: (permit) => {
|
|
2011
1996
|
if (ValidationUtils.isExpired(permit)) {
|
|
2012
1997
|
return { valid: false, error: "expired" };
|
|
2013
1998
|
}
|
|
@@ -2015,6 +2000,34 @@ var ValidationUtils = {
|
|
|
2015
2000
|
return { valid: false, error: "not-signed" };
|
|
2016
2001
|
}
|
|
2017
2002
|
return { valid: true, error: null };
|
|
2003
|
+
},
|
|
2004
|
+
/**
|
|
2005
|
+
* Asserts that a permit is signed and not expired.
|
|
2006
|
+
*
|
|
2007
|
+
* Throws `Error` with message:
|
|
2008
|
+
* - `Permit is expired`
|
|
2009
|
+
* - `Permit is not signed`
|
|
2010
|
+
*/
|
|
2011
|
+
assertSignedAndNotExpired: (permit) => {
|
|
2012
|
+
const result = ValidationUtils.isSignedAndNotExpired(permit);
|
|
2013
|
+
if (result.valid)
|
|
2014
|
+
return;
|
|
2015
|
+
if (result.error === "expired") {
|
|
2016
|
+
throw new Error("Permit is expired");
|
|
2017
|
+
}
|
|
2018
|
+
if (result.error === "not-signed") {
|
|
2019
|
+
throw new Error("Permit is not signed");
|
|
2020
|
+
}
|
|
2021
|
+
throw new Error("Permit is invalid");
|
|
2022
|
+
},
|
|
2023
|
+
isValid: (permit) => {
|
|
2024
|
+
const schema = permit.type === "self" ? SelfPermitValidator : permit.type === "sharing" ? SharingPermitValidator : permit.type === "recipient" ? ImportPermitValidator : null;
|
|
2025
|
+
if (schema == null)
|
|
2026
|
+
return { valid: false, error: "invalid-schema" };
|
|
2027
|
+
const schemaResult = schema.safeParse(permit);
|
|
2028
|
+
if (!schemaResult.success)
|
|
2029
|
+
return { valid: false, error: "invalid-schema" };
|
|
2030
|
+
return ValidationUtils.isSignedAndNotExpired(permit);
|
|
2018
2031
|
}
|
|
2019
2032
|
};
|
|
2020
2033
|
|
|
@@ -2396,9 +2409,9 @@ var PermitUtils = {
|
|
|
2396
2409
|
};
|
|
2397
2410
|
},
|
|
2398
2411
|
/**
|
|
2399
|
-
* Validate a permit
|
|
2412
|
+
* Validate a permit (schema-level validation)
|
|
2400
2413
|
*/
|
|
2401
|
-
|
|
2414
|
+
validateSchema: (permit) => {
|
|
2402
2415
|
if (permit.type === "self") {
|
|
2403
2416
|
return validateSelfPermit(permit);
|
|
2404
2417
|
} else if (permit.type === "sharing") {
|
|
@@ -2409,12 +2422,27 @@ var PermitUtils = {
|
|
|
2409
2422
|
throw new Error("Invalid permit type");
|
|
2410
2423
|
}
|
|
2411
2424
|
},
|
|
2425
|
+
/**
|
|
2426
|
+
* Validate a permit (holistic validation).
|
|
2427
|
+
*
|
|
2428
|
+
* This validates:
|
|
2429
|
+
* - Permit schema (shape + invariants)
|
|
2430
|
+
* - Permit is signed
|
|
2431
|
+
* - Permit is not expired
|
|
2432
|
+
*
|
|
2433
|
+
* For schema-only validation, use `validateSchema(permit)`.
|
|
2434
|
+
*/
|
|
2435
|
+
validate: (permit) => {
|
|
2436
|
+
const validated = PermitUtils.validateSchema(permit);
|
|
2437
|
+
ValidationUtils.assertSignedAndNotExpired(validated);
|
|
2438
|
+
return validated;
|
|
2439
|
+
},
|
|
2412
2440
|
/**
|
|
2413
2441
|
* Get the permission object from a permit (for use in contracts)
|
|
2414
2442
|
*/
|
|
2415
2443
|
getPermission: (permit, skipValidation = false) => {
|
|
2416
2444
|
if (!skipValidation) {
|
|
2417
|
-
PermitUtils.
|
|
2445
|
+
PermitUtils.validateSchema(permit);
|
|
2418
2446
|
}
|
|
2419
2447
|
return {
|
|
2420
2448
|
issuer: permit.issuer,
|
|
@@ -2480,8 +2508,17 @@ var PermitUtils = {
|
|
|
2480
2508
|
return ValidationUtils.isSigned(permit);
|
|
2481
2509
|
},
|
|
2482
2510
|
/**
|
|
2483
|
-
* Check if permit is
|
|
2511
|
+
* Check if permit is signed and not expired
|
|
2484
2512
|
*/
|
|
2513
|
+
isSignedAndNotExpired: (permit) => {
|
|
2514
|
+
return ValidationUtils.isSignedAndNotExpired(permit);
|
|
2515
|
+
},
|
|
2516
|
+
/**
|
|
2517
|
+
* Assert that permit is signed and not expired
|
|
2518
|
+
*/
|
|
2519
|
+
assertSignedAndNotExpired: (permit) => {
|
|
2520
|
+
return ValidationUtils.assertSignedAndNotExpired(permit);
|
|
2521
|
+
},
|
|
2485
2522
|
isValid: (permit) => {
|
|
2486
2523
|
return ValidationUtils.isValid(permit);
|
|
2487
2524
|
},
|
|
@@ -2659,19 +2696,22 @@ var importShared = async (options, publicClient, walletClient) => {
|
|
|
2659
2696
|
var getHash = (permit) => {
|
|
2660
2697
|
return PermitUtils.getHash(permit);
|
|
2661
2698
|
};
|
|
2699
|
+
var exportShared = (permit) => {
|
|
2700
|
+
return PermitUtils.export(permit);
|
|
2701
|
+
};
|
|
2662
2702
|
var serialize = (permit) => {
|
|
2663
2703
|
return PermitUtils.serialize(permit);
|
|
2664
2704
|
};
|
|
2665
2705
|
var deserialize = (serialized) => {
|
|
2666
2706
|
return PermitUtils.deserialize(serialized);
|
|
2667
2707
|
};
|
|
2668
|
-
var getPermit2 =
|
|
2708
|
+
var getPermit2 = (chainId, account, hash) => {
|
|
2669
2709
|
return permitStore.getPermit(chainId, account, hash);
|
|
2670
2710
|
};
|
|
2671
|
-
var getPermits2 =
|
|
2711
|
+
var getPermits2 = (chainId, account) => {
|
|
2672
2712
|
return permitStore.getPermits(chainId, account);
|
|
2673
2713
|
};
|
|
2674
|
-
var getActivePermit2 =
|
|
2714
|
+
var getActivePermit2 = (chainId, account) => {
|
|
2675
2715
|
return permitStore.getActivePermit(chainId, account);
|
|
2676
2716
|
};
|
|
2677
2717
|
var getActivePermitHash2 = (chainId, account) => {
|
|
@@ -2709,6 +2749,7 @@ var permits = {
|
|
|
2709
2749
|
getOrCreateSelfPermit,
|
|
2710
2750
|
getOrCreateSharingPermit,
|
|
2711
2751
|
getHash,
|
|
2752
|
+
export: exportShared,
|
|
2712
2753
|
serialize,
|
|
2713
2754
|
deserialize,
|
|
2714
2755
|
getPermit: getPermit2,
|
|
@@ -2951,9 +2992,19 @@ async function cofheMocksDecryptForView(ctHash, utype, permit, publicClient) {
|
|
|
2951
2992
|
return unsealed;
|
|
2952
2993
|
}
|
|
2953
2994
|
|
|
2995
|
+
// core/decrypt/polling.ts
|
|
2996
|
+
function computeMinuteRampPollIntervalMs(elapsedMs, params) {
|
|
2997
|
+
const elapsedSeconds = Math.floor(elapsedMs / 1e3);
|
|
2998
|
+
const intervalSeconds = 1 + Math.floor(elapsedSeconds / 60);
|
|
2999
|
+
const intervalMs = intervalSeconds * 1e3;
|
|
3000
|
+
return Math.min(params.maxIntervalMs, Math.max(params.minIntervalMs, intervalMs));
|
|
3001
|
+
}
|
|
3002
|
+
|
|
2954
3003
|
// core/decrypt/tnSealOutputV2.ts
|
|
2955
3004
|
var POLL_INTERVAL_MS = 1e3;
|
|
2956
|
-
var
|
|
3005
|
+
var POLL_MAX_INTERVAL_MS = 1e4;
|
|
3006
|
+
var SEAL_OUTPUT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
3007
|
+
var SUBMIT_RETRY_INTERVAL_MS = 1e3;
|
|
2957
3008
|
function numberArrayToUint8Array(arr) {
|
|
2958
3009
|
return new Uint8Array(arr);
|
|
2959
3010
|
}
|
|
@@ -2970,93 +3021,193 @@ function convertSealedData(sealed) {
|
|
|
2970
3021
|
nonce: numberArrayToUint8Array(sealed.nonce)
|
|
2971
3022
|
};
|
|
2972
3023
|
}
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
method: "POST",
|
|
2983
|
-
headers: {
|
|
2984
|
-
"Content-Type": "application/json"
|
|
2985
|
-
},
|
|
2986
|
-
body: JSON.stringify(body)
|
|
2987
|
-
});
|
|
2988
|
-
} catch (e) {
|
|
2989
|
-
throw new CofheError({
|
|
2990
|
-
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
2991
|
-
message: `sealOutput request failed`,
|
|
2992
|
-
hint: "Ensure the threshold network URL is valid and reachable.",
|
|
2993
|
-
cause: e instanceof Error ? e : void 0,
|
|
2994
|
-
context: {
|
|
2995
|
-
thresholdNetworkUrl,
|
|
2996
|
-
body
|
|
2997
|
-
}
|
|
2998
|
-
});
|
|
3024
|
+
function getSealedDataFromSubmitResponse(value) {
|
|
3025
|
+
if (value.sealed)
|
|
3026
|
+
return value.sealed;
|
|
3027
|
+
if (Array.isArray(value.sealed_data) && Array.isArray(value.ephemeral_public_key) && Array.isArray(value.nonce)) {
|
|
3028
|
+
return {
|
|
3029
|
+
data: value.sealed_data,
|
|
3030
|
+
public_key: value.ephemeral_public_key,
|
|
3031
|
+
nonce: value.nonce
|
|
3032
|
+
};
|
|
2999
3033
|
}
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
errorMessage = response.statusText || errorMessage;
|
|
3007
|
-
}
|
|
3034
|
+
return void 0;
|
|
3035
|
+
}
|
|
3036
|
+
function parseCompletedSealOutputResponse(params) {
|
|
3037
|
+
const { value, thresholdNetworkUrl, requestId } = params;
|
|
3038
|
+
if (value.is_succeed === false) {
|
|
3039
|
+
const errorMessage = value.error_message || "Unknown error";
|
|
3008
3040
|
throw new CofheError({
|
|
3009
3041
|
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
3010
3042
|
message: `sealOutput request failed: ${errorMessage}`,
|
|
3011
|
-
hint: "Check the threshold network URL and request parameters.",
|
|
3012
3043
|
context: {
|
|
3013
3044
|
thresholdNetworkUrl,
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
body
|
|
3045
|
+
requestId,
|
|
3046
|
+
response: value
|
|
3017
3047
|
}
|
|
3018
3048
|
});
|
|
3019
3049
|
}
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
submitResponse = await response.json();
|
|
3023
|
-
} catch (e) {
|
|
3050
|
+
const sealed = "sealed" in value ? value.sealed : getSealedDataFromSubmitResponse(value);
|
|
3051
|
+
if (!sealed) {
|
|
3024
3052
|
throw new CofheError({
|
|
3025
|
-
code: "
|
|
3026
|
-
message: `
|
|
3027
|
-
cause: e instanceof Error ? e : void 0,
|
|
3053
|
+
code: "SEAL_OUTPUT_RETURNED_NULL" /* SealOutputReturnedNull */,
|
|
3054
|
+
message: `sealOutput request completed but returned no sealed data`,
|
|
3028
3055
|
context: {
|
|
3029
3056
|
thresholdNetworkUrl,
|
|
3030
|
-
|
|
3057
|
+
requestId,
|
|
3058
|
+
response: value
|
|
3031
3059
|
}
|
|
3032
3060
|
});
|
|
3033
3061
|
}
|
|
3034
|
-
|
|
3062
|
+
return convertSealedData(sealed);
|
|
3063
|
+
}
|
|
3064
|
+
async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, permission, overallStartTime, onPoll) {
|
|
3065
|
+
const body = {
|
|
3066
|
+
ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
|
|
3067
|
+
host_chain_id: chainId,
|
|
3068
|
+
permit: permission
|
|
3069
|
+
};
|
|
3070
|
+
let attemptIndex = 0;
|
|
3071
|
+
for (; ; ) {
|
|
3072
|
+
let response;
|
|
3073
|
+
try {
|
|
3074
|
+
response = await fetch(`${thresholdNetworkUrl}/v2/sealoutput`, {
|
|
3075
|
+
method: "POST",
|
|
3076
|
+
headers: {
|
|
3077
|
+
"Content-Type": "application/json"
|
|
3078
|
+
},
|
|
3079
|
+
body: JSON.stringify(body)
|
|
3080
|
+
});
|
|
3081
|
+
} catch (e) {
|
|
3082
|
+
throw new CofheError({
|
|
3083
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
3084
|
+
message: `sealOutput request failed`,
|
|
3085
|
+
hint: "Ensure the threshold network URL is valid and reachable.",
|
|
3086
|
+
cause: e instanceof Error ? e : void 0,
|
|
3087
|
+
context: {
|
|
3088
|
+
thresholdNetworkUrl,
|
|
3089
|
+
body,
|
|
3090
|
+
attemptIndex
|
|
3091
|
+
}
|
|
3092
|
+
});
|
|
3093
|
+
}
|
|
3094
|
+
if (!response.ok) {
|
|
3095
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
3096
|
+
try {
|
|
3097
|
+
const errorBody = await response.json();
|
|
3098
|
+
errorMessage = errorBody.error_message || errorBody.message || errorMessage;
|
|
3099
|
+
} catch {
|
|
3100
|
+
errorMessage = response.statusText || errorMessage;
|
|
3101
|
+
}
|
|
3102
|
+
throw new CofheError({
|
|
3103
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
3104
|
+
message: `sealOutput request failed: ${errorMessage}`,
|
|
3105
|
+
hint: "Check the threshold network URL and request parameters.",
|
|
3106
|
+
context: {
|
|
3107
|
+
thresholdNetworkUrl,
|
|
3108
|
+
status: response.status,
|
|
3109
|
+
statusText: response.statusText,
|
|
3110
|
+
body,
|
|
3111
|
+
attemptIndex
|
|
3112
|
+
}
|
|
3113
|
+
});
|
|
3114
|
+
}
|
|
3115
|
+
let submitResponse;
|
|
3116
|
+
if (response.status !== 204) {
|
|
3117
|
+
try {
|
|
3118
|
+
submitResponse = await response.json();
|
|
3119
|
+
} catch (e) {
|
|
3120
|
+
throw new CofheError({
|
|
3121
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
3122
|
+
message: `Failed to parse sealOutput submit response`,
|
|
3123
|
+
cause: e instanceof Error ? e : void 0,
|
|
3124
|
+
context: {
|
|
3125
|
+
thresholdNetworkUrl,
|
|
3126
|
+
body,
|
|
3127
|
+
attemptIndex
|
|
3128
|
+
}
|
|
3129
|
+
});
|
|
3130
|
+
}
|
|
3131
|
+
if (getSealedDataFromSubmitResponse(submitResponse)) {
|
|
3132
|
+
return {
|
|
3133
|
+
kind: "completed",
|
|
3134
|
+
sealed: parseCompletedSealOutputResponse({
|
|
3135
|
+
value: submitResponse,
|
|
3136
|
+
thresholdNetworkUrl,
|
|
3137
|
+
requestId: submitResponse.request_id
|
|
3138
|
+
})
|
|
3139
|
+
};
|
|
3140
|
+
}
|
|
3141
|
+
if (submitResponse.request_id) {
|
|
3142
|
+
return { kind: "request_id", requestId: submitResponse.request_id };
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
3145
|
+
if (response.status === 204) {
|
|
3146
|
+
const elapsedMs = Date.now() - overallStartTime;
|
|
3147
|
+
if (elapsedMs > SEAL_OUTPUT_TIMEOUT_MS) {
|
|
3148
|
+
throw new CofheError({
|
|
3149
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
3150
|
+
message: `sealOutput submit retried without receiving request_id for ${SEAL_OUTPUT_TIMEOUT_MS}ms`,
|
|
3151
|
+
hint: "The ciphertext may still be propagating. Try again later.",
|
|
3152
|
+
context: {
|
|
3153
|
+
thresholdNetworkUrl,
|
|
3154
|
+
body,
|
|
3155
|
+
attemptIndex,
|
|
3156
|
+
timeoutMs: SEAL_OUTPUT_TIMEOUT_MS,
|
|
3157
|
+
submitResponse,
|
|
3158
|
+
status: response.status
|
|
3159
|
+
}
|
|
3160
|
+
});
|
|
3161
|
+
}
|
|
3162
|
+
onPoll?.({
|
|
3163
|
+
operation: "sealoutput",
|
|
3164
|
+
requestId: "",
|
|
3165
|
+
attemptIndex,
|
|
3166
|
+
elapsedMs,
|
|
3167
|
+
intervalMs: SUBMIT_RETRY_INTERVAL_MS,
|
|
3168
|
+
timeoutMs: SEAL_OUTPUT_TIMEOUT_MS
|
|
3169
|
+
});
|
|
3170
|
+
await new Promise((resolve) => setTimeout(resolve, SUBMIT_RETRY_INTERVAL_MS));
|
|
3171
|
+
attemptIndex += 1;
|
|
3172
|
+
continue;
|
|
3173
|
+
}
|
|
3035
3174
|
throw new CofheError({
|
|
3036
3175
|
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
3037
3176
|
message: `sealOutput submit response missing request_id`,
|
|
3038
3177
|
context: {
|
|
3039
3178
|
thresholdNetworkUrl,
|
|
3040
3179
|
body,
|
|
3041
|
-
submitResponse
|
|
3180
|
+
submitResponse,
|
|
3181
|
+
attemptIndex
|
|
3042
3182
|
}
|
|
3043
3183
|
});
|
|
3044
3184
|
}
|
|
3045
|
-
return submitResponse.request_id;
|
|
3046
3185
|
}
|
|
3047
|
-
async function pollSealOutputStatus(thresholdNetworkUrl, requestId) {
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3186
|
+
async function pollSealOutputStatus(thresholdNetworkUrl, requestId, overallStartTime, onPoll) {
|
|
3187
|
+
let attemptIndex = 0;
|
|
3188
|
+
while (true) {
|
|
3189
|
+
const elapsedMs = Date.now() - overallStartTime;
|
|
3190
|
+
const intervalMs = computeMinuteRampPollIntervalMs(elapsedMs, {
|
|
3191
|
+
minIntervalMs: POLL_INTERVAL_MS,
|
|
3192
|
+
maxIntervalMs: POLL_MAX_INTERVAL_MS
|
|
3193
|
+
});
|
|
3194
|
+
onPoll?.({
|
|
3195
|
+
operation: "sealoutput",
|
|
3196
|
+
requestId,
|
|
3197
|
+
attemptIndex,
|
|
3198
|
+
elapsedMs,
|
|
3199
|
+
intervalMs,
|
|
3200
|
+
timeoutMs: SEAL_OUTPUT_TIMEOUT_MS
|
|
3201
|
+
});
|
|
3202
|
+
if (elapsedMs > SEAL_OUTPUT_TIMEOUT_MS) {
|
|
3052
3203
|
throw new CofheError({
|
|
3053
3204
|
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
3054
|
-
message: `sealOutput polling timed out after ${
|
|
3205
|
+
message: `sealOutput polling timed out after ${SEAL_OUTPUT_TIMEOUT_MS}ms`,
|
|
3055
3206
|
hint: "The request may still be processing. Try again later.",
|
|
3056
3207
|
context: {
|
|
3057
3208
|
thresholdNetworkUrl,
|
|
3058
3209
|
requestId,
|
|
3059
|
-
timeoutMs:
|
|
3210
|
+
timeoutMs: SEAL_OUTPUT_TIMEOUT_MS
|
|
3060
3211
|
}
|
|
3061
3212
|
});
|
|
3062
3213
|
}
|
|
@@ -3125,32 +3276,14 @@ async function pollSealOutputStatus(thresholdNetworkUrl, requestId) {
|
|
|
3125
3276
|
});
|
|
3126
3277
|
}
|
|
3127
3278
|
if (statusResponse.status === "COMPLETED") {
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
context: {
|
|
3134
|
-
thresholdNetworkUrl,
|
|
3135
|
-
requestId,
|
|
3136
|
-
statusResponse
|
|
3137
|
-
}
|
|
3138
|
-
});
|
|
3139
|
-
}
|
|
3140
|
-
if (!statusResponse.sealed) {
|
|
3141
|
-
throw new CofheError({
|
|
3142
|
-
code: "SEAL_OUTPUT_RETURNED_NULL" /* SealOutputReturnedNull */,
|
|
3143
|
-
message: `sealOutput request completed but returned no sealed data`,
|
|
3144
|
-
context: {
|
|
3145
|
-
thresholdNetworkUrl,
|
|
3146
|
-
requestId,
|
|
3147
|
-
statusResponse
|
|
3148
|
-
}
|
|
3149
|
-
});
|
|
3150
|
-
}
|
|
3151
|
-
return convertSealedData(statusResponse.sealed);
|
|
3279
|
+
return parseCompletedSealOutputResponse({
|
|
3280
|
+
value: statusResponse,
|
|
3281
|
+
thresholdNetworkUrl,
|
|
3282
|
+
requestId
|
|
3283
|
+
});
|
|
3152
3284
|
}
|
|
3153
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
3285
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
3286
|
+
attemptIndex += 1;
|
|
3154
3287
|
}
|
|
3155
3288
|
throw new CofheError({
|
|
3156
3289
|
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
@@ -3161,9 +3294,21 @@ async function pollSealOutputStatus(thresholdNetworkUrl, requestId) {
|
|
|
3161
3294
|
}
|
|
3162
3295
|
});
|
|
3163
3296
|
}
|
|
3164
|
-
async function tnSealOutputV2(
|
|
3165
|
-
const
|
|
3166
|
-
|
|
3297
|
+
async function tnSealOutputV2(params) {
|
|
3298
|
+
const { thresholdNetworkUrl, ctHash, chainId, permission, onPoll } = params;
|
|
3299
|
+
const overallStartTime = Date.now();
|
|
3300
|
+
const submitResult = await submitSealOutputRequest(
|
|
3301
|
+
thresholdNetworkUrl,
|
|
3302
|
+
ctHash,
|
|
3303
|
+
chainId,
|
|
3304
|
+
permission,
|
|
3305
|
+
overallStartTime,
|
|
3306
|
+
onPoll
|
|
3307
|
+
);
|
|
3308
|
+
if (submitResult.kind === "completed") {
|
|
3309
|
+
return submitResult.sealed;
|
|
3310
|
+
}
|
|
3311
|
+
return await pollSealOutputStatus(thresholdNetworkUrl, submitResult.requestId, overallStartTime, onPoll);
|
|
3167
3312
|
}
|
|
3168
3313
|
|
|
3169
3314
|
// core/decrypt/decryptForViewBuilder.ts
|
|
@@ -3172,6 +3317,7 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
3172
3317
|
utype;
|
|
3173
3318
|
permitHash;
|
|
3174
3319
|
permit;
|
|
3320
|
+
pollCallback;
|
|
3175
3321
|
constructor(params) {
|
|
3176
3322
|
super({
|
|
3177
3323
|
config: params.config,
|
|
@@ -3228,6 +3374,10 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
3228
3374
|
getAccount() {
|
|
3229
3375
|
return this.account;
|
|
3230
3376
|
}
|
|
3377
|
+
onPoll(callback) {
|
|
3378
|
+
this.pollCallback = callback;
|
|
3379
|
+
return this;
|
|
3380
|
+
}
|
|
3231
3381
|
withPermit(permitOrPermitHash) {
|
|
3232
3382
|
if (typeof permitOrPermitHash === "string") {
|
|
3233
3383
|
this.permitHash = permitOrPermitHash;
|
|
@@ -3351,7 +3501,13 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
3351
3501
|
this.assertPublicClient();
|
|
3352
3502
|
const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
|
|
3353
3503
|
const permission = PermitUtils.getPermission(permit, true);
|
|
3354
|
-
const sealed = await tnSealOutputV2(
|
|
3504
|
+
const sealed = await tnSealOutputV2({
|
|
3505
|
+
ctHash: this.ctHash,
|
|
3506
|
+
chainId: this.chainId,
|
|
3507
|
+
permission,
|
|
3508
|
+
thresholdNetworkUrl,
|
|
3509
|
+
onPoll: this.pollCallback
|
|
3510
|
+
});
|
|
3355
3511
|
return PermitUtils.unseal(permit, sealed);
|
|
3356
3512
|
}
|
|
3357
3513
|
/**
|
|
@@ -3379,7 +3535,6 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
3379
3535
|
this.validateUtypeOrThrow();
|
|
3380
3536
|
const permit = await this.getResolvedPermit();
|
|
3381
3537
|
PermitUtils.validate(permit);
|
|
3382
|
-
PermitUtils.isValid(permit);
|
|
3383
3538
|
const chainId = permit._signedDomain.chainId;
|
|
3384
3539
|
let unsealed;
|
|
3385
3540
|
if (chainId === hardhat2.id) {
|
|
@@ -3390,6 +3545,9 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
3390
3545
|
return convertViaUtype(this.utype, unsealed);
|
|
3391
3546
|
}
|
|
3392
3547
|
};
|
|
3548
|
+
var UINT_TYPE_MASK = 0x7fn;
|
|
3549
|
+
var TYPE_BYTE_OFFSET = 8n;
|
|
3550
|
+
var getEncryptionTypeFromCtHash = (ctHash) => Number(ctHash >> TYPE_BYTE_OFFSET & UINT_TYPE_MASK);
|
|
3393
3551
|
async function cofheMocksDecryptForTx(ctHash, utype, permit, publicClient) {
|
|
3394
3552
|
let allowed;
|
|
3395
3553
|
let error;
|
|
@@ -3427,7 +3585,13 @@ async function cofheMocksDecryptForTx(ctHash, utype, permit, publicClient) {
|
|
|
3427
3585
|
message: `mocks decryptForTx call failed: ACL Access Denied (NotAllowed)`
|
|
3428
3586
|
});
|
|
3429
3587
|
}
|
|
3430
|
-
const
|
|
3588
|
+
const chainId = publicClient.chain?.id ?? await publicClient.getChainId();
|
|
3589
|
+
const normalizedCtHash = BigInt(ctHash);
|
|
3590
|
+
const encryptionType = getEncryptionTypeFromCtHash(normalizedCtHash);
|
|
3591
|
+
const packed = viem.encodePacked(
|
|
3592
|
+
["uint256", "uint32", "uint64", "uint256"],
|
|
3593
|
+
[decryptedValue, encryptionType, BigInt(chainId), normalizedCtHash]
|
|
3594
|
+
);
|
|
3431
3595
|
const messageHash = viem.keccak256(packed);
|
|
3432
3596
|
const signature = await accounts.sign({
|
|
3433
3597
|
hash: messageHash,
|
|
@@ -3440,7 +3604,7 @@ async function cofheMocksDecryptForTx(ctHash, utype, permit, publicClient) {
|
|
|
3440
3604
|
signature
|
|
3441
3605
|
};
|
|
3442
3606
|
}
|
|
3443
|
-
function
|
|
3607
|
+
function normalizeTnSignature(signature) {
|
|
3444
3608
|
if (typeof signature !== "string") {
|
|
3445
3609
|
throw new CofheError({
|
|
3446
3610
|
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
@@ -3496,141 +3660,384 @@ function parseDecryptedBytesToBigInt(decrypted) {
|
|
|
3496
3660
|
}
|
|
3497
3661
|
return BigInt(`0x${hex}`);
|
|
3498
3662
|
}
|
|
3499
|
-
|
|
3663
|
+
|
|
3664
|
+
// core/decrypt/tnDecryptV2.ts
|
|
3665
|
+
var POLL_INTERVAL_MS2 = 1e3;
|
|
3666
|
+
var POLL_MAX_INTERVAL_MS2 = 1e4;
|
|
3667
|
+
var DECRYPT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
3668
|
+
var SUBMIT_RETRY_INTERVAL_MS2 = 1e3;
|
|
3669
|
+
function assertDecryptSubmitResponseV2(value) {
|
|
3500
3670
|
if (value == null || typeof value !== "object") {
|
|
3501
3671
|
throw new CofheError({
|
|
3502
3672
|
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3503
|
-
message: "decrypt response must be a JSON object",
|
|
3673
|
+
message: "decrypt submit response must be a JSON object",
|
|
3504
3674
|
context: {
|
|
3505
3675
|
value
|
|
3506
3676
|
}
|
|
3507
3677
|
});
|
|
3508
3678
|
}
|
|
3509
3679
|
const v = value;
|
|
3510
|
-
|
|
3511
|
-
const signature = v.signature;
|
|
3512
|
-
const encryptionType = v.encryption_type;
|
|
3513
|
-
const errorMessage = v.error_message;
|
|
3514
|
-
if (!Array.isArray(decrypted)) {
|
|
3680
|
+
if (v.request_id !== null && typeof v.request_id !== "string") {
|
|
3515
3681
|
throw new CofheError({
|
|
3516
|
-
code: "
|
|
3517
|
-
message: "decrypt response
|
|
3518
|
-
context: {
|
|
3682
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3683
|
+
message: "decrypt submit response has invalid request_id",
|
|
3684
|
+
context: {
|
|
3685
|
+
value
|
|
3686
|
+
}
|
|
3519
3687
|
});
|
|
3520
3688
|
}
|
|
3521
|
-
|
|
3689
|
+
return {
|
|
3690
|
+
request_id: v.request_id ?? null,
|
|
3691
|
+
status: typeof v.status === "string" ? v.status : void 0,
|
|
3692
|
+
is_succeed: typeof v.is_succeed === "boolean" ? v.is_succeed : void 0,
|
|
3693
|
+
decrypted: Array.isArray(v.decrypted) ? v.decrypted : void 0,
|
|
3694
|
+
signature: typeof v.signature === "string" ? v.signature : void 0,
|
|
3695
|
+
encryption_type: typeof v.encryption_type === "number" ? v.encryption_type : void 0,
|
|
3696
|
+
error_message: typeof v.error_message === "string" || v.error_message === null ? v.error_message : void 0,
|
|
3697
|
+
message: typeof v.message === "string" ? v.message : void 0
|
|
3698
|
+
};
|
|
3699
|
+
}
|
|
3700
|
+
function parseCompletedDecryptResponseV2(params) {
|
|
3701
|
+
const { value, thresholdNetworkUrl, requestId } = params;
|
|
3702
|
+
if (value.is_succeed === false) {
|
|
3703
|
+
const errorMessage = value.error_message || "Unknown error";
|
|
3522
3704
|
throw new CofheError({
|
|
3523
|
-
code: "
|
|
3524
|
-
message:
|
|
3525
|
-
context: {
|
|
3705
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3706
|
+
message: `decrypt request failed: ${errorMessage}`,
|
|
3707
|
+
context: {
|
|
3708
|
+
thresholdNetworkUrl,
|
|
3709
|
+
requestId,
|
|
3710
|
+
response: value
|
|
3711
|
+
}
|
|
3526
3712
|
});
|
|
3527
3713
|
}
|
|
3528
|
-
if (
|
|
3714
|
+
if (value.error_message) {
|
|
3529
3715
|
throw new CofheError({
|
|
3530
3716
|
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3531
|
-
message:
|
|
3532
|
-
context: {
|
|
3717
|
+
message: `decrypt request failed: ${value.error_message}`,
|
|
3718
|
+
context: {
|
|
3719
|
+
thresholdNetworkUrl,
|
|
3720
|
+
requestId,
|
|
3721
|
+
response: value
|
|
3722
|
+
}
|
|
3533
3723
|
});
|
|
3534
3724
|
}
|
|
3535
|
-
if (!(
|
|
3725
|
+
if (!Array.isArray(value.decrypted)) {
|
|
3536
3726
|
throw new CofheError({
|
|
3537
|
-
code: "
|
|
3538
|
-
message: "decrypt response
|
|
3539
|
-
context: {
|
|
3727
|
+
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
3728
|
+
message: "decrypt completed but response missing <decrypted> byte array",
|
|
3729
|
+
context: {
|
|
3730
|
+
thresholdNetworkUrl,
|
|
3731
|
+
requestId,
|
|
3732
|
+
response: value
|
|
3733
|
+
}
|
|
3540
3734
|
});
|
|
3541
3735
|
}
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
encryption_type: encryptionType,
|
|
3546
|
-
error_message: errorMessage
|
|
3547
|
-
};
|
|
3736
|
+
const decryptedValue = parseDecryptedBytesToBigInt(value.decrypted);
|
|
3737
|
+
const signature = normalizeTnSignature(value.signature);
|
|
3738
|
+
return { decryptedValue, signature };
|
|
3548
3739
|
}
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
let response;
|
|
3558
|
-
try {
|
|
3559
|
-
response = await fetch(`${thresholdNetworkUrl}/decrypt`, {
|
|
3560
|
-
method: "POST",
|
|
3561
|
-
headers: {
|
|
3562
|
-
"Content-Type": "application/json"
|
|
3563
|
-
},
|
|
3564
|
-
body: JSON.stringify(body)
|
|
3740
|
+
function assertDecryptStatusResponseV2(value) {
|
|
3741
|
+
if (value == null || typeof value !== "object") {
|
|
3742
|
+
throw new CofheError({
|
|
3743
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3744
|
+
message: "decrypt status response must be a JSON object",
|
|
3745
|
+
context: {
|
|
3746
|
+
value
|
|
3747
|
+
}
|
|
3565
3748
|
});
|
|
3566
|
-
}
|
|
3749
|
+
}
|
|
3750
|
+
const v = value;
|
|
3751
|
+
const requestId = v.request_id;
|
|
3752
|
+
const status = v.status;
|
|
3753
|
+
const submittedAt = v.submitted_at;
|
|
3754
|
+
if (typeof requestId !== "string" || requestId.trim().length === 0) {
|
|
3567
3755
|
throw new CofheError({
|
|
3568
3756
|
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3569
|
-
message:
|
|
3570
|
-
hint: "Ensure the threshold network URL is valid and reachable.",
|
|
3571
|
-
cause: e instanceof Error ? e : void 0,
|
|
3757
|
+
message: "decrypt status response missing request_id",
|
|
3572
3758
|
context: {
|
|
3573
|
-
|
|
3574
|
-
body
|
|
3759
|
+
value
|
|
3575
3760
|
}
|
|
3576
3761
|
});
|
|
3577
3762
|
}
|
|
3578
|
-
|
|
3579
|
-
if (!response.ok) {
|
|
3580
|
-
let errorMessage = response.statusText || `HTTP ${response.status}`;
|
|
3581
|
-
try {
|
|
3582
|
-
const errorBody = JSON.parse(responseText);
|
|
3583
|
-
const maybeMessage = errorBody.error_message || errorBody.message;
|
|
3584
|
-
if (typeof maybeMessage === "string" && maybeMessage.length > 0)
|
|
3585
|
-
errorMessage = maybeMessage;
|
|
3586
|
-
} catch {
|
|
3587
|
-
const trimmed = responseText.trim();
|
|
3588
|
-
if (trimmed.length > 0)
|
|
3589
|
-
errorMessage = trimmed;
|
|
3590
|
-
}
|
|
3763
|
+
if (status !== "PROCESSING" && status !== "COMPLETED") {
|
|
3591
3764
|
throw new CofheError({
|
|
3592
3765
|
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3593
|
-
message:
|
|
3594
|
-
hint: "Check the threshold network URL and request parameters.",
|
|
3766
|
+
message: "decrypt status response has invalid status",
|
|
3595
3767
|
context: {
|
|
3596
|
-
|
|
3597
|
-
status
|
|
3598
|
-
statusText: response.statusText,
|
|
3599
|
-
body,
|
|
3600
|
-
responseText
|
|
3768
|
+
value,
|
|
3769
|
+
status
|
|
3601
3770
|
}
|
|
3602
3771
|
});
|
|
3603
3772
|
}
|
|
3604
|
-
|
|
3605
|
-
try {
|
|
3606
|
-
rawJson = JSON.parse(responseText);
|
|
3607
|
-
} catch (e) {
|
|
3773
|
+
if (typeof submittedAt !== "string" || submittedAt.trim().length === 0) {
|
|
3608
3774
|
throw new CofheError({
|
|
3609
3775
|
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3610
|
-
message:
|
|
3611
|
-
cause: e instanceof Error ? e : void 0,
|
|
3776
|
+
message: "decrypt status response missing submitted_at",
|
|
3612
3777
|
context: {
|
|
3613
|
-
|
|
3614
|
-
body,
|
|
3615
|
-
responseText
|
|
3778
|
+
value
|
|
3616
3779
|
}
|
|
3617
3780
|
});
|
|
3618
3781
|
}
|
|
3619
|
-
|
|
3620
|
-
|
|
3782
|
+
return value;
|
|
3783
|
+
}
|
|
3784
|
+
async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, permission, overallStartTime, onPoll) {
|
|
3785
|
+
const body = {
|
|
3786
|
+
ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
|
|
3787
|
+
host_chain_id: chainId
|
|
3788
|
+
};
|
|
3789
|
+
if (permission) {
|
|
3790
|
+
body.permit = permission;
|
|
3791
|
+
}
|
|
3792
|
+
let attemptIndex = 0;
|
|
3793
|
+
for (; ; ) {
|
|
3794
|
+
let response;
|
|
3795
|
+
try {
|
|
3796
|
+
response = await fetch(`${thresholdNetworkUrl}/v2/decrypt`, {
|
|
3797
|
+
method: "POST",
|
|
3798
|
+
headers: {
|
|
3799
|
+
"Content-Type": "application/json"
|
|
3800
|
+
},
|
|
3801
|
+
body: JSON.stringify(body)
|
|
3802
|
+
});
|
|
3803
|
+
} catch (e) {
|
|
3804
|
+
throw new CofheError({
|
|
3805
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3806
|
+
message: `decrypt request failed`,
|
|
3807
|
+
hint: "Ensure the threshold network URL is valid and reachable.",
|
|
3808
|
+
cause: e instanceof Error ? e : void 0,
|
|
3809
|
+
context: {
|
|
3810
|
+
thresholdNetworkUrl,
|
|
3811
|
+
body,
|
|
3812
|
+
attemptIndex
|
|
3813
|
+
}
|
|
3814
|
+
});
|
|
3815
|
+
}
|
|
3816
|
+
if (!response.ok) {
|
|
3817
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
3818
|
+
try {
|
|
3819
|
+
const errorBody = await response.json();
|
|
3820
|
+
const maybeMessage = errorBody.error_message || errorBody.message;
|
|
3821
|
+
if (typeof maybeMessage === "string" && maybeMessage.length > 0)
|
|
3822
|
+
errorMessage = maybeMessage;
|
|
3823
|
+
} catch {
|
|
3824
|
+
errorMessage = response.statusText || errorMessage;
|
|
3825
|
+
}
|
|
3826
|
+
throw new CofheError({
|
|
3827
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3828
|
+
message: `decrypt request failed: ${errorMessage}`,
|
|
3829
|
+
hint: "Check the threshold network URL and request parameters.",
|
|
3830
|
+
context: {
|
|
3831
|
+
thresholdNetworkUrl,
|
|
3832
|
+
status: response.status,
|
|
3833
|
+
statusText: response.statusText,
|
|
3834
|
+
body,
|
|
3835
|
+
attemptIndex
|
|
3836
|
+
}
|
|
3837
|
+
});
|
|
3838
|
+
}
|
|
3839
|
+
let submitResponse;
|
|
3840
|
+
if (response.status !== 204) {
|
|
3841
|
+
let rawJson;
|
|
3842
|
+
try {
|
|
3843
|
+
rawJson = await response.json();
|
|
3844
|
+
} catch (e) {
|
|
3845
|
+
throw new CofheError({
|
|
3846
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3847
|
+
message: `Failed to parse decrypt submit response`,
|
|
3848
|
+
cause: e instanceof Error ? e : void 0,
|
|
3849
|
+
context: {
|
|
3850
|
+
thresholdNetworkUrl,
|
|
3851
|
+
body,
|
|
3852
|
+
attemptIndex
|
|
3853
|
+
}
|
|
3854
|
+
});
|
|
3855
|
+
}
|
|
3856
|
+
submitResponse = assertDecryptSubmitResponseV2(rawJson);
|
|
3857
|
+
if (Array.isArray(submitResponse.decrypted) && typeof submitResponse.signature === "string") {
|
|
3858
|
+
return {
|
|
3859
|
+
kind: "completed",
|
|
3860
|
+
...parseCompletedDecryptResponseV2({
|
|
3861
|
+
value: submitResponse,
|
|
3862
|
+
thresholdNetworkUrl,
|
|
3863
|
+
requestId: submitResponse.request_id
|
|
3864
|
+
})
|
|
3865
|
+
};
|
|
3866
|
+
}
|
|
3867
|
+
if (submitResponse.request_id) {
|
|
3868
|
+
return { kind: "request_id", requestId: submitResponse.request_id };
|
|
3869
|
+
}
|
|
3870
|
+
}
|
|
3871
|
+
if (response.status === 204) {
|
|
3872
|
+
const elapsedMs = Date.now() - overallStartTime;
|
|
3873
|
+
if (elapsedMs > DECRYPT_TIMEOUT_MS) {
|
|
3874
|
+
throw new CofheError({
|
|
3875
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3876
|
+
message: `decrypt submit retried without receiving request_id for ${DECRYPT_TIMEOUT_MS}ms`,
|
|
3877
|
+
hint: "The ciphertext may still be propagating. Try again later.",
|
|
3878
|
+
context: {
|
|
3879
|
+
thresholdNetworkUrl,
|
|
3880
|
+
body,
|
|
3881
|
+
attemptIndex,
|
|
3882
|
+
timeoutMs: DECRYPT_TIMEOUT_MS,
|
|
3883
|
+
submitResponse,
|
|
3884
|
+
status: response.status
|
|
3885
|
+
}
|
|
3886
|
+
});
|
|
3887
|
+
}
|
|
3888
|
+
onPoll?.({
|
|
3889
|
+
operation: "decrypt",
|
|
3890
|
+
requestId: "",
|
|
3891
|
+
attemptIndex,
|
|
3892
|
+
elapsedMs,
|
|
3893
|
+
intervalMs: SUBMIT_RETRY_INTERVAL_MS2,
|
|
3894
|
+
timeoutMs: DECRYPT_TIMEOUT_MS
|
|
3895
|
+
});
|
|
3896
|
+
await new Promise((resolve) => setTimeout(resolve, SUBMIT_RETRY_INTERVAL_MS2));
|
|
3897
|
+
attemptIndex += 1;
|
|
3898
|
+
continue;
|
|
3899
|
+
}
|
|
3621
3900
|
throw new CofheError({
|
|
3622
3901
|
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3623
|
-
message: `decrypt
|
|
3902
|
+
message: `decrypt submit response missing request_id`,
|
|
3624
3903
|
context: {
|
|
3625
3904
|
thresholdNetworkUrl,
|
|
3626
3905
|
body,
|
|
3627
|
-
|
|
3906
|
+
submitResponse,
|
|
3907
|
+
attemptIndex
|
|
3628
3908
|
}
|
|
3629
3909
|
});
|
|
3630
3910
|
}
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3911
|
+
}
|
|
3912
|
+
async function pollDecryptStatusV2(thresholdNetworkUrl, requestId, overallStartTime, onPoll) {
|
|
3913
|
+
let attemptIndex = 0;
|
|
3914
|
+
while (true) {
|
|
3915
|
+
const elapsedMs = Date.now() - overallStartTime;
|
|
3916
|
+
const intervalMs = computeMinuteRampPollIntervalMs(elapsedMs, {
|
|
3917
|
+
minIntervalMs: POLL_INTERVAL_MS2,
|
|
3918
|
+
maxIntervalMs: POLL_MAX_INTERVAL_MS2
|
|
3919
|
+
});
|
|
3920
|
+
onPoll?.({
|
|
3921
|
+
operation: "decrypt",
|
|
3922
|
+
requestId,
|
|
3923
|
+
attemptIndex,
|
|
3924
|
+
elapsedMs,
|
|
3925
|
+
intervalMs,
|
|
3926
|
+
timeoutMs: DECRYPT_TIMEOUT_MS
|
|
3927
|
+
});
|
|
3928
|
+
if (elapsedMs > DECRYPT_TIMEOUT_MS) {
|
|
3929
|
+
throw new CofheError({
|
|
3930
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3931
|
+
message: `decrypt polling timed out after ${DECRYPT_TIMEOUT_MS}ms`,
|
|
3932
|
+
hint: "The request may still be processing. Try again later.",
|
|
3933
|
+
context: {
|
|
3934
|
+
thresholdNetworkUrl,
|
|
3935
|
+
requestId,
|
|
3936
|
+
timeoutMs: DECRYPT_TIMEOUT_MS
|
|
3937
|
+
}
|
|
3938
|
+
});
|
|
3939
|
+
}
|
|
3940
|
+
let response;
|
|
3941
|
+
try {
|
|
3942
|
+
response = await fetch(`${thresholdNetworkUrl}/v2/decrypt/${requestId}`, {
|
|
3943
|
+
method: "GET",
|
|
3944
|
+
headers: {
|
|
3945
|
+
"Content-Type": "application/json"
|
|
3946
|
+
}
|
|
3947
|
+
});
|
|
3948
|
+
} catch (e) {
|
|
3949
|
+
throw new CofheError({
|
|
3950
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3951
|
+
message: `decrypt status poll failed`,
|
|
3952
|
+
hint: "Ensure the threshold network URL is valid and reachable.",
|
|
3953
|
+
cause: e instanceof Error ? e : void 0,
|
|
3954
|
+
context: {
|
|
3955
|
+
thresholdNetworkUrl,
|
|
3956
|
+
requestId
|
|
3957
|
+
}
|
|
3958
|
+
});
|
|
3959
|
+
}
|
|
3960
|
+
if (response.status === 404) {
|
|
3961
|
+
throw new CofheError({
|
|
3962
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3963
|
+
message: `decrypt request not found: ${requestId}`,
|
|
3964
|
+
hint: "The request may have expired or been invalid.",
|
|
3965
|
+
context: {
|
|
3966
|
+
thresholdNetworkUrl,
|
|
3967
|
+
requestId
|
|
3968
|
+
}
|
|
3969
|
+
});
|
|
3970
|
+
}
|
|
3971
|
+
if (!response.ok) {
|
|
3972
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
3973
|
+
try {
|
|
3974
|
+
const errorBody = await response.json();
|
|
3975
|
+
const maybeMessage = errorBody.error_message || errorBody.message;
|
|
3976
|
+
if (typeof maybeMessage === "string" && maybeMessage.length > 0)
|
|
3977
|
+
errorMessage = maybeMessage;
|
|
3978
|
+
} catch {
|
|
3979
|
+
errorMessage = response.statusText || errorMessage;
|
|
3980
|
+
}
|
|
3981
|
+
throw new CofheError({
|
|
3982
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3983
|
+
message: `decrypt status poll failed: ${errorMessage}`,
|
|
3984
|
+
context: {
|
|
3985
|
+
thresholdNetworkUrl,
|
|
3986
|
+
requestId,
|
|
3987
|
+
status: response.status,
|
|
3988
|
+
statusText: response.statusText
|
|
3989
|
+
}
|
|
3990
|
+
});
|
|
3991
|
+
}
|
|
3992
|
+
let rawJson;
|
|
3993
|
+
try {
|
|
3994
|
+
rawJson = await response.json();
|
|
3995
|
+
} catch (e) {
|
|
3996
|
+
throw new CofheError({
|
|
3997
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
3998
|
+
message: `Failed to parse decrypt status response`,
|
|
3999
|
+
cause: e instanceof Error ? e : void 0,
|
|
4000
|
+
context: {
|
|
4001
|
+
thresholdNetworkUrl,
|
|
4002
|
+
requestId
|
|
4003
|
+
}
|
|
4004
|
+
});
|
|
4005
|
+
}
|
|
4006
|
+
const statusResponse = assertDecryptStatusResponseV2(rawJson);
|
|
4007
|
+
if (statusResponse.status === "COMPLETED") {
|
|
4008
|
+
return parseCompletedDecryptResponseV2({
|
|
4009
|
+
value: statusResponse,
|
|
4010
|
+
thresholdNetworkUrl,
|
|
4011
|
+
requestId
|
|
4012
|
+
});
|
|
4013
|
+
}
|
|
4014
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
4015
|
+
attemptIndex += 1;
|
|
4016
|
+
}
|
|
4017
|
+
throw new CofheError({
|
|
4018
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
4019
|
+
message: "Polling loop exited unexpectedly",
|
|
4020
|
+
context: {
|
|
4021
|
+
thresholdNetworkUrl,
|
|
4022
|
+
requestId
|
|
4023
|
+
}
|
|
4024
|
+
});
|
|
4025
|
+
}
|
|
4026
|
+
async function tnDecryptV2(params) {
|
|
4027
|
+
const { thresholdNetworkUrl, ctHash, chainId, permission, onPoll } = params;
|
|
4028
|
+
const overallStartTime = Date.now();
|
|
4029
|
+
const submitResult = await submitDecryptRequestV2(
|
|
4030
|
+
thresholdNetworkUrl,
|
|
4031
|
+
ctHash,
|
|
4032
|
+
chainId,
|
|
4033
|
+
permission,
|
|
4034
|
+
overallStartTime,
|
|
4035
|
+
onPoll
|
|
4036
|
+
);
|
|
4037
|
+
if (submitResult.kind === "completed") {
|
|
4038
|
+
return submitResult;
|
|
4039
|
+
}
|
|
4040
|
+
return await pollDecryptStatusV2(thresholdNetworkUrl, submitResult.requestId, overallStartTime, onPoll);
|
|
3634
4041
|
}
|
|
3635
4042
|
|
|
3636
4043
|
// core/decrypt/decryptForTxBuilder.ts
|
|
@@ -3639,6 +4046,7 @@ var DecryptForTxBuilder = class extends BaseBuilder {
|
|
|
3639
4046
|
permitHash;
|
|
3640
4047
|
permit;
|
|
3641
4048
|
permitSelection = "unset";
|
|
4049
|
+
pollCallback;
|
|
3642
4050
|
constructor(params) {
|
|
3643
4051
|
super({
|
|
3644
4052
|
config: params.config,
|
|
@@ -3664,6 +4072,10 @@ var DecryptForTxBuilder = class extends BaseBuilder {
|
|
|
3664
4072
|
getAccount() {
|
|
3665
4073
|
return this.account;
|
|
3666
4074
|
}
|
|
4075
|
+
onPoll(callback) {
|
|
4076
|
+
this.pollCallback = callback;
|
|
4077
|
+
return this;
|
|
4078
|
+
}
|
|
3667
4079
|
withPermit(permitOrPermitHash) {
|
|
3668
4080
|
if (this.permitSelection === "with-permit") {
|
|
3669
4081
|
throw new CofheError({
|
|
@@ -3791,7 +4203,13 @@ var DecryptForTxBuilder = class extends BaseBuilder {
|
|
|
3791
4203
|
this.assertPublicClient();
|
|
3792
4204
|
const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
|
|
3793
4205
|
const permission = permit ? PermitUtils.getPermission(permit, true) : null;
|
|
3794
|
-
const { decryptedValue, signature } = await
|
|
4206
|
+
const { decryptedValue, signature } = await tnDecryptV2({
|
|
4207
|
+
ctHash: this.ctHash,
|
|
4208
|
+
chainId: this.chainId,
|
|
4209
|
+
permission,
|
|
4210
|
+
thresholdNetworkUrl,
|
|
4211
|
+
onPoll: this.pollCallback
|
|
4212
|
+
});
|
|
3795
4213
|
return {
|
|
3796
4214
|
ctHash: this.ctHash,
|
|
3797
4215
|
decryptedValue,
|
|
@@ -3809,7 +4227,6 @@ var DecryptForTxBuilder = class extends BaseBuilder {
|
|
|
3809
4227
|
const permit = await this.getResolvedPermit();
|
|
3810
4228
|
if (permit !== null) {
|
|
3811
4229
|
PermitUtils.validate(permit);
|
|
3812
|
-
PermitUtils.isValid(permit);
|
|
3813
4230
|
const chainId = permit._signedDomain.chainId;
|
|
3814
4231
|
if (chainId === hardhat2.id) {
|
|
3815
4232
|
return await this.mocksDecryptForTx(permit);
|
|
@@ -3830,6 +4247,35 @@ var DecryptForTxBuilder = class extends BaseBuilder {
|
|
|
3830
4247
|
}
|
|
3831
4248
|
}
|
|
3832
4249
|
};
|
|
4250
|
+
var decryptResultSignerAbi = viem.parseAbi(["function decryptResultSigner() view returns (address)"]);
|
|
4251
|
+
var UINT_TYPE_MASK2 = 0x7fn;
|
|
4252
|
+
var TYPE_BYTE_OFFSET2 = 8n;
|
|
4253
|
+
var getEncryptionTypeFromCtHash2 = (ctHash) => Number(ctHash >> TYPE_BYTE_OFFSET2 & UINT_TYPE_MASK2);
|
|
4254
|
+
var buildDecryptResultHash = (ctHash, cleartext, chainId) => {
|
|
4255
|
+
const encryptionType = getEncryptionTypeFromCtHash2(ctHash);
|
|
4256
|
+
return viem.keccak256(
|
|
4257
|
+
viem.encodePacked(["uint256", "uint32", "uint64", "uint256"], [cleartext, encryptionType, BigInt(chainId), ctHash])
|
|
4258
|
+
);
|
|
4259
|
+
};
|
|
4260
|
+
async function verifyDecryptResult(handle, cleartext, signature, publicClient) {
|
|
4261
|
+
const chainId = publicClient.chain?.id ?? await publicClient.getChainId();
|
|
4262
|
+
const expectedSigner = await publicClient.readContract({
|
|
4263
|
+
address: TASK_MANAGER_ADDRESS,
|
|
4264
|
+
abi: decryptResultSignerAbi,
|
|
4265
|
+
functionName: "decryptResultSigner",
|
|
4266
|
+
args: []
|
|
4267
|
+
});
|
|
4268
|
+
if (viem.isAddressEqual(expectedSigner, viem.zeroAddress))
|
|
4269
|
+
return true;
|
|
4270
|
+
const ctHash = BigInt(handle);
|
|
4271
|
+
const messageHash = buildDecryptResultHash(ctHash, cleartext, chainId);
|
|
4272
|
+
try {
|
|
4273
|
+
const recovered = await viem.recoverAddress({ hash: messageHash, signature });
|
|
4274
|
+
return viem.isAddressEqual(recovered, expectedSigner);
|
|
4275
|
+
} catch {
|
|
4276
|
+
return false;
|
|
4277
|
+
}
|
|
4278
|
+
}
|
|
3833
4279
|
|
|
3834
4280
|
// core/client.ts
|
|
3835
4281
|
var InitialConnectStore = {
|
|
@@ -3948,6 +4394,11 @@ function createCofheClientBase(opts) {
|
|
|
3948
4394
|
requireConnected: _requireConnected
|
|
3949
4395
|
});
|
|
3950
4396
|
}
|
|
4397
|
+
function verifyDecryptResult2(handle, cleartext, signature) {
|
|
4398
|
+
_requireConnected();
|
|
4399
|
+
const { publicClient } = connectStore.getState();
|
|
4400
|
+
return verifyDecryptResult(handle, cleartext, signature, publicClient);
|
|
4401
|
+
}
|
|
3951
4402
|
const _getChainIdAndAccount = (chainId, account) => {
|
|
3952
4403
|
const state = connectStore.getState();
|
|
3953
4404
|
const _chainId = chainId ?? state.chainId;
|
|
@@ -3999,19 +4450,19 @@ function createCofheClientBase(opts) {
|
|
|
3999
4450
|
return permits.getOrCreateSharingPermit(publicClient, walletClient, options, _chainId, _account);
|
|
4000
4451
|
},
|
|
4001
4452
|
// Retrieval methods (auto-fill chainId/account)
|
|
4002
|
-
getPermit:
|
|
4453
|
+
getPermit: (hash, chainId, account) => {
|
|
4003
4454
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4004
4455
|
return permits.getPermit(_chainId, _account, hash);
|
|
4005
4456
|
},
|
|
4006
|
-
getPermits:
|
|
4457
|
+
getPermits: (chainId, account) => {
|
|
4007
4458
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4008
4459
|
return permits.getPermits(_chainId, _account);
|
|
4009
4460
|
},
|
|
4010
|
-
getActivePermit:
|
|
4461
|
+
getActivePermit: (chainId, account) => {
|
|
4011
4462
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4012
4463
|
return permits.getActivePermit(_chainId, _account);
|
|
4013
4464
|
},
|
|
4014
|
-
getActivePermitHash:
|
|
4465
|
+
getActivePermitHash: (chainId, account) => {
|
|
4015
4466
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
4016
4467
|
return permits.getActivePermitHash(_chainId, _account);
|
|
4017
4468
|
},
|
|
@@ -4030,6 +4481,7 @@ function createCofheClientBase(opts) {
|
|
|
4030
4481
|
},
|
|
4031
4482
|
// Utils (no context needed)
|
|
4032
4483
|
getHash: permits.getHash,
|
|
4484
|
+
export: permits.export,
|
|
4033
4485
|
serialize: permits.serialize,
|
|
4034
4486
|
deserialize: permits.deserialize
|
|
4035
4487
|
};
|
|
@@ -4058,6 +4510,7 @@ function createCofheClientBase(opts) {
|
|
|
4058
4510
|
*/
|
|
4059
4511
|
decryptHandle: decryptForView,
|
|
4060
4512
|
decryptForTx,
|
|
4513
|
+
verifyDecryptResult: verifyDecryptResult2,
|
|
4061
4514
|
permits: clientPermits
|
|
4062
4515
|
// Add SDK-specific methods below that require connection
|
|
4063
4516
|
// Example:
|
|
@@ -4086,6 +4539,8 @@ exports.MOCKS_ZK_VERIFIER_SIGNER_ADDRESS = MOCKS_ZK_VERIFIER_SIGNER_ADDRESS;
|
|
|
4086
4539
|
exports.MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY = MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY;
|
|
4087
4540
|
exports.TASK_MANAGER_ADDRESS = TASK_MANAGER_ADDRESS;
|
|
4088
4541
|
exports.TEST_BED_ADDRESS = TEST_BED_ADDRESS;
|
|
4542
|
+
exports.TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT = TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT;
|
|
4543
|
+
exports.TFHE_RS_ZK_MAX_BITS = TFHE_RS_ZK_MAX_BITS;
|
|
4089
4544
|
exports.assertCorrectEncryptedItemInput = assertCorrectEncryptedItemInput;
|
|
4090
4545
|
exports.createCofheClientBase = createCofheClientBase;
|
|
4091
4546
|
exports.createCofheConfigBase = createCofheConfigBase;
|
|
@@ -4096,4 +4551,5 @@ exports.getCofheConfigItem = getCofheConfigItem;
|
|
|
4096
4551
|
exports.isCofheError = isCofheError;
|
|
4097
4552
|
exports.isEncryptableItem = isEncryptableItem;
|
|
4098
4553
|
exports.isLastEncryptionStep = isLastEncryptionStep;
|
|
4554
|
+
exports.verifyDecryptResult = verifyDecryptResult;
|
|
4099
4555
|
exports.zkProveWithWorker = zkProveWithWorker;
|