@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
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { hardhat as hardhat$1 } from './chunk-TBLR7NNE.js';
|
|
2
|
-
import { permitStore, PermitUtils
|
|
2
|
+
import { permitStore, PermitUtils } from './chunk-MRCKUMOS.js';
|
|
3
|
+
import { TFHE_RS_KEY_VERSION, TASK_MANAGER_ADDRESS, TFHE_RS_ZK_MAX_BITS, TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT, MOCKS_THRESHOLD_NETWORK_ADDRESS, MOCKS_DECRYPT_RESULT_SIGNER_PRIVATE_KEY, MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY, MOCKS_ZK_VERIFIER_ADDRESS } from './chunk-4FP4V35O.js';
|
|
3
4
|
import { createStore } from 'zustand/vanilla';
|
|
4
|
-
import { encodePacked, keccak256, createWalletClient, http, getAddress, parseSignature, serializeSignature } from 'viem';
|
|
5
|
+
import { parseAbi, isAddressEqual, zeroAddress, recoverAddress, encodePacked, keccak256, createWalletClient, http, getAddress, parseSignature, serializeSignature } from 'viem';
|
|
5
6
|
import { hardhat } from 'viem/chains';
|
|
6
7
|
import { sign, privateKeyToAccount } from 'viem/accounts';
|
|
7
8
|
import { z } from 'zod';
|
|
@@ -372,7 +373,6 @@ var MAX_UINT32 = 4294967295n;
|
|
|
372
373
|
var MAX_UINT64 = 18446744073709551615n;
|
|
373
374
|
var MAX_UINT128 = 340282366920938463463374607431768211455n;
|
|
374
375
|
var MAX_UINT160 = 1461501637330902918203684832716283019655932542975n;
|
|
375
|
-
var MAX_ENCRYPTABLE_BITS = 2048;
|
|
376
376
|
var zkPack = (items, builder) => {
|
|
377
377
|
let totalBits = 0;
|
|
378
378
|
for (const item of items) {
|
|
@@ -436,14 +436,14 @@ var zkPack = (items, builder) => {
|
|
|
436
436
|
}
|
|
437
437
|
}
|
|
438
438
|
}
|
|
439
|
-
if (totalBits >
|
|
439
|
+
if (totalBits > TFHE_RS_ZK_MAX_BITS) {
|
|
440
440
|
throw new CofheError({
|
|
441
441
|
code: "ZK_PACK_FAILED" /* ZkPackFailed */,
|
|
442
|
-
message: `Total bits ${totalBits} exceeds ${
|
|
443
|
-
hint: `Ensure that the total bits of the items to encrypt does not exceed ${
|
|
442
|
+
message: `Total bits ${totalBits} exceeds ${TFHE_RS_ZK_MAX_BITS}`,
|
|
443
|
+
hint: `Ensure that the total bits of the items to encrypt does not exceed ${TFHE_RS_ZK_MAX_BITS}`,
|
|
444
444
|
context: {
|
|
445
445
|
totalBits,
|
|
446
|
-
maxBits:
|
|
446
|
+
maxBits: TFHE_RS_ZK_MAX_BITS,
|
|
447
447
|
items
|
|
448
448
|
}
|
|
449
449
|
});
|
|
@@ -462,7 +462,7 @@ var zkProve = async (builder, crs, metadata) => {
|
|
|
462
462
|
1
|
|
463
463
|
// ZkComputeLoad.Verify
|
|
464
464
|
);
|
|
465
|
-
resolve(compactList.
|
|
465
|
+
resolve(compactList.safe_serialize(TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT));
|
|
466
466
|
}, 0);
|
|
467
467
|
});
|
|
468
468
|
};
|
|
@@ -679,14 +679,14 @@ async function cofheMocksCheckEncryptableBits(items) {
|
|
|
679
679
|
}
|
|
680
680
|
}
|
|
681
681
|
}
|
|
682
|
-
if (totalBits >
|
|
682
|
+
if (totalBits > TFHE_RS_ZK_MAX_BITS) {
|
|
683
683
|
throw new CofheError({
|
|
684
684
|
code: "ZK_PACK_FAILED" /* ZkPackFailed */,
|
|
685
|
-
message: `Total bits ${totalBits} exceeds ${
|
|
686
|
-
hint: `Ensure that the total bits of the items to encrypt does not exceed ${
|
|
685
|
+
message: `Total bits ${totalBits} exceeds ${TFHE_RS_ZK_MAX_BITS}`,
|
|
686
|
+
hint: `Ensure that the total bits of the items to encrypt does not exceed ${TFHE_RS_ZK_MAX_BITS}`,
|
|
687
687
|
context: {
|
|
688
688
|
totalBits,
|
|
689
|
-
maxBits:
|
|
689
|
+
maxBits: TFHE_RS_ZK_MAX_BITS,
|
|
690
690
|
items
|
|
691
691
|
}
|
|
692
692
|
});
|
|
@@ -924,6 +924,7 @@ function getThresholdNetworkUrlOrThrow(config, chainId) {
|
|
|
924
924
|
}
|
|
925
925
|
return url;
|
|
926
926
|
}
|
|
927
|
+
var KEYSTORE_NAME = `cofhesdk-keys-v${TFHE_RS_KEY_VERSION}`;
|
|
927
928
|
function isValidPersistedState(state) {
|
|
928
929
|
if (state && typeof state === "object") {
|
|
929
930
|
if ("fhe" in state && "crs" in state) {
|
|
@@ -978,7 +979,7 @@ function createKeysStore(storage) {
|
|
|
978
979
|
};
|
|
979
980
|
const clearKeysStorage = async () => {
|
|
980
981
|
if (storage) {
|
|
981
|
-
await storage.removeItem(
|
|
982
|
+
await storage.removeItem(KEYSTORE_NAME);
|
|
982
983
|
}
|
|
983
984
|
};
|
|
984
985
|
const rehydrateKeysStore = async () => {
|
|
@@ -1008,7 +1009,7 @@ function createStoreWithPersit(storage) {
|
|
|
1008
1009
|
if (_error)
|
|
1009
1010
|
throw new Error(`onRehydrateStorage: Error rehydrating keys store: ${_error}`);
|
|
1010
1011
|
},
|
|
1011
|
-
name:
|
|
1012
|
+
name: KEYSTORE_NAME,
|
|
1012
1013
|
storage: createJSONStorage(() => storage),
|
|
1013
1014
|
merge: (persistedState, currentState) => {
|
|
1014
1015
|
const persisted = isValidPersistedState(persistedState) ? persistedState : DEFAULT_KEYS_STORE;
|
|
@@ -1672,19 +1673,22 @@ var importShared = async (options, publicClient, walletClient) => {
|
|
|
1672
1673
|
var getHash = (permit) => {
|
|
1673
1674
|
return PermitUtils.getHash(permit);
|
|
1674
1675
|
};
|
|
1676
|
+
var exportShared = (permit) => {
|
|
1677
|
+
return PermitUtils.export(permit);
|
|
1678
|
+
};
|
|
1675
1679
|
var serialize = (permit) => {
|
|
1676
1680
|
return PermitUtils.serialize(permit);
|
|
1677
1681
|
};
|
|
1678
1682
|
var deserialize = (serialized) => {
|
|
1679
1683
|
return PermitUtils.deserialize(serialized);
|
|
1680
1684
|
};
|
|
1681
|
-
var getPermit =
|
|
1685
|
+
var getPermit = (chainId, account, hash) => {
|
|
1682
1686
|
return permitStore.getPermit(chainId, account, hash);
|
|
1683
1687
|
};
|
|
1684
|
-
var getPermits =
|
|
1688
|
+
var getPermits = (chainId, account) => {
|
|
1685
1689
|
return permitStore.getPermits(chainId, account);
|
|
1686
1690
|
};
|
|
1687
|
-
var getActivePermit =
|
|
1691
|
+
var getActivePermit = (chainId, account) => {
|
|
1688
1692
|
return permitStore.getActivePermit(chainId, account);
|
|
1689
1693
|
};
|
|
1690
1694
|
var getActivePermitHash = (chainId, account) => {
|
|
@@ -1722,6 +1726,7 @@ var permits = {
|
|
|
1722
1726
|
getOrCreateSelfPermit,
|
|
1723
1727
|
getOrCreateSharingPermit,
|
|
1724
1728
|
getHash,
|
|
1729
|
+
export: exportShared,
|
|
1725
1730
|
serialize,
|
|
1726
1731
|
deserialize,
|
|
1727
1732
|
getPermit,
|
|
@@ -1964,9 +1969,19 @@ async function cofheMocksDecryptForView(ctHash, utype, permit, publicClient) {
|
|
|
1964
1969
|
return unsealed;
|
|
1965
1970
|
}
|
|
1966
1971
|
|
|
1972
|
+
// core/decrypt/polling.ts
|
|
1973
|
+
function computeMinuteRampPollIntervalMs(elapsedMs, params) {
|
|
1974
|
+
const elapsedSeconds = Math.floor(elapsedMs / 1e3);
|
|
1975
|
+
const intervalSeconds = 1 + Math.floor(elapsedSeconds / 60);
|
|
1976
|
+
const intervalMs = intervalSeconds * 1e3;
|
|
1977
|
+
return Math.min(params.maxIntervalMs, Math.max(params.minIntervalMs, intervalMs));
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1967
1980
|
// core/decrypt/tnSealOutputV2.ts
|
|
1968
1981
|
var POLL_INTERVAL_MS = 1e3;
|
|
1969
|
-
var
|
|
1982
|
+
var POLL_MAX_INTERVAL_MS = 1e4;
|
|
1983
|
+
var SEAL_OUTPUT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
1984
|
+
var SUBMIT_RETRY_INTERVAL_MS = 1e3;
|
|
1970
1985
|
function numberArrayToUint8Array(arr) {
|
|
1971
1986
|
return new Uint8Array(arr);
|
|
1972
1987
|
}
|
|
@@ -1983,93 +1998,193 @@ function convertSealedData(sealed) {
|
|
|
1983
1998
|
nonce: numberArrayToUint8Array(sealed.nonce)
|
|
1984
1999
|
};
|
|
1985
2000
|
}
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
method: "POST",
|
|
1996
|
-
headers: {
|
|
1997
|
-
"Content-Type": "application/json"
|
|
1998
|
-
},
|
|
1999
|
-
body: JSON.stringify(body)
|
|
2000
|
-
});
|
|
2001
|
-
} catch (e) {
|
|
2002
|
-
throw new CofheError({
|
|
2003
|
-
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
2004
|
-
message: `sealOutput request failed`,
|
|
2005
|
-
hint: "Ensure the threshold network URL is valid and reachable.",
|
|
2006
|
-
cause: e instanceof Error ? e : void 0,
|
|
2007
|
-
context: {
|
|
2008
|
-
thresholdNetworkUrl,
|
|
2009
|
-
body
|
|
2010
|
-
}
|
|
2011
|
-
});
|
|
2001
|
+
function getSealedDataFromSubmitResponse(value) {
|
|
2002
|
+
if (value.sealed)
|
|
2003
|
+
return value.sealed;
|
|
2004
|
+
if (Array.isArray(value.sealed_data) && Array.isArray(value.ephemeral_public_key) && Array.isArray(value.nonce)) {
|
|
2005
|
+
return {
|
|
2006
|
+
data: value.sealed_data,
|
|
2007
|
+
public_key: value.ephemeral_public_key,
|
|
2008
|
+
nonce: value.nonce
|
|
2009
|
+
};
|
|
2012
2010
|
}
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
errorMessage = response.statusText || errorMessage;
|
|
2020
|
-
}
|
|
2011
|
+
return void 0;
|
|
2012
|
+
}
|
|
2013
|
+
function parseCompletedSealOutputResponse(params) {
|
|
2014
|
+
const { value, thresholdNetworkUrl, requestId } = params;
|
|
2015
|
+
if (value.is_succeed === false) {
|
|
2016
|
+
const errorMessage = value.error_message || "Unknown error";
|
|
2021
2017
|
throw new CofheError({
|
|
2022
2018
|
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
2023
2019
|
message: `sealOutput request failed: ${errorMessage}`,
|
|
2024
|
-
hint: "Check the threshold network URL and request parameters.",
|
|
2025
2020
|
context: {
|
|
2026
2021
|
thresholdNetworkUrl,
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
body
|
|
2022
|
+
requestId,
|
|
2023
|
+
response: value
|
|
2030
2024
|
}
|
|
2031
2025
|
});
|
|
2032
2026
|
}
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
submitResponse = await response.json();
|
|
2036
|
-
} catch (e) {
|
|
2027
|
+
const sealed = "sealed" in value ? value.sealed : getSealedDataFromSubmitResponse(value);
|
|
2028
|
+
if (!sealed) {
|
|
2037
2029
|
throw new CofheError({
|
|
2038
|
-
code: "
|
|
2039
|
-
message: `
|
|
2040
|
-
cause: e instanceof Error ? e : void 0,
|
|
2030
|
+
code: "SEAL_OUTPUT_RETURNED_NULL" /* SealOutputReturnedNull */,
|
|
2031
|
+
message: `sealOutput request completed but returned no sealed data`,
|
|
2041
2032
|
context: {
|
|
2042
2033
|
thresholdNetworkUrl,
|
|
2043
|
-
|
|
2034
|
+
requestId,
|
|
2035
|
+
response: value
|
|
2044
2036
|
}
|
|
2045
2037
|
});
|
|
2046
2038
|
}
|
|
2047
|
-
|
|
2039
|
+
return convertSealedData(sealed);
|
|
2040
|
+
}
|
|
2041
|
+
async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, permission, overallStartTime, onPoll) {
|
|
2042
|
+
const body = {
|
|
2043
|
+
ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
|
|
2044
|
+
host_chain_id: chainId,
|
|
2045
|
+
permit: permission
|
|
2046
|
+
};
|
|
2047
|
+
let attemptIndex = 0;
|
|
2048
|
+
for (; ; ) {
|
|
2049
|
+
let response;
|
|
2050
|
+
try {
|
|
2051
|
+
response = await fetch(`${thresholdNetworkUrl}/v2/sealoutput`, {
|
|
2052
|
+
method: "POST",
|
|
2053
|
+
headers: {
|
|
2054
|
+
"Content-Type": "application/json"
|
|
2055
|
+
},
|
|
2056
|
+
body: JSON.stringify(body)
|
|
2057
|
+
});
|
|
2058
|
+
} catch (e) {
|
|
2059
|
+
throw new CofheError({
|
|
2060
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
2061
|
+
message: `sealOutput request failed`,
|
|
2062
|
+
hint: "Ensure the threshold network URL is valid and reachable.",
|
|
2063
|
+
cause: e instanceof Error ? e : void 0,
|
|
2064
|
+
context: {
|
|
2065
|
+
thresholdNetworkUrl,
|
|
2066
|
+
body,
|
|
2067
|
+
attemptIndex
|
|
2068
|
+
}
|
|
2069
|
+
});
|
|
2070
|
+
}
|
|
2071
|
+
if (!response.ok) {
|
|
2072
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
2073
|
+
try {
|
|
2074
|
+
const errorBody = await response.json();
|
|
2075
|
+
errorMessage = errorBody.error_message || errorBody.message || errorMessage;
|
|
2076
|
+
} catch {
|
|
2077
|
+
errorMessage = response.statusText || errorMessage;
|
|
2078
|
+
}
|
|
2079
|
+
throw new CofheError({
|
|
2080
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
2081
|
+
message: `sealOutput request failed: ${errorMessage}`,
|
|
2082
|
+
hint: "Check the threshold network URL and request parameters.",
|
|
2083
|
+
context: {
|
|
2084
|
+
thresholdNetworkUrl,
|
|
2085
|
+
status: response.status,
|
|
2086
|
+
statusText: response.statusText,
|
|
2087
|
+
body,
|
|
2088
|
+
attemptIndex
|
|
2089
|
+
}
|
|
2090
|
+
});
|
|
2091
|
+
}
|
|
2092
|
+
let submitResponse;
|
|
2093
|
+
if (response.status !== 204) {
|
|
2094
|
+
try {
|
|
2095
|
+
submitResponse = await response.json();
|
|
2096
|
+
} catch (e) {
|
|
2097
|
+
throw new CofheError({
|
|
2098
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
2099
|
+
message: `Failed to parse sealOutput submit response`,
|
|
2100
|
+
cause: e instanceof Error ? e : void 0,
|
|
2101
|
+
context: {
|
|
2102
|
+
thresholdNetworkUrl,
|
|
2103
|
+
body,
|
|
2104
|
+
attemptIndex
|
|
2105
|
+
}
|
|
2106
|
+
});
|
|
2107
|
+
}
|
|
2108
|
+
if (getSealedDataFromSubmitResponse(submitResponse)) {
|
|
2109
|
+
return {
|
|
2110
|
+
kind: "completed",
|
|
2111
|
+
sealed: parseCompletedSealOutputResponse({
|
|
2112
|
+
value: submitResponse,
|
|
2113
|
+
thresholdNetworkUrl,
|
|
2114
|
+
requestId: submitResponse.request_id
|
|
2115
|
+
})
|
|
2116
|
+
};
|
|
2117
|
+
}
|
|
2118
|
+
if (submitResponse.request_id) {
|
|
2119
|
+
return { kind: "request_id", requestId: submitResponse.request_id };
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
if (response.status === 204) {
|
|
2123
|
+
const elapsedMs = Date.now() - overallStartTime;
|
|
2124
|
+
if (elapsedMs > SEAL_OUTPUT_TIMEOUT_MS) {
|
|
2125
|
+
throw new CofheError({
|
|
2126
|
+
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
2127
|
+
message: `sealOutput submit retried without receiving request_id for ${SEAL_OUTPUT_TIMEOUT_MS}ms`,
|
|
2128
|
+
hint: "The ciphertext may still be propagating. Try again later.",
|
|
2129
|
+
context: {
|
|
2130
|
+
thresholdNetworkUrl,
|
|
2131
|
+
body,
|
|
2132
|
+
attemptIndex,
|
|
2133
|
+
timeoutMs: SEAL_OUTPUT_TIMEOUT_MS,
|
|
2134
|
+
submitResponse,
|
|
2135
|
+
status: response.status
|
|
2136
|
+
}
|
|
2137
|
+
});
|
|
2138
|
+
}
|
|
2139
|
+
onPoll?.({
|
|
2140
|
+
operation: "sealoutput",
|
|
2141
|
+
requestId: "",
|
|
2142
|
+
attemptIndex,
|
|
2143
|
+
elapsedMs,
|
|
2144
|
+
intervalMs: SUBMIT_RETRY_INTERVAL_MS,
|
|
2145
|
+
timeoutMs: SEAL_OUTPUT_TIMEOUT_MS
|
|
2146
|
+
});
|
|
2147
|
+
await new Promise((resolve) => setTimeout(resolve, SUBMIT_RETRY_INTERVAL_MS));
|
|
2148
|
+
attemptIndex += 1;
|
|
2149
|
+
continue;
|
|
2150
|
+
}
|
|
2048
2151
|
throw new CofheError({
|
|
2049
2152
|
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
2050
2153
|
message: `sealOutput submit response missing request_id`,
|
|
2051
2154
|
context: {
|
|
2052
2155
|
thresholdNetworkUrl,
|
|
2053
2156
|
body,
|
|
2054
|
-
submitResponse
|
|
2157
|
+
submitResponse,
|
|
2158
|
+
attemptIndex
|
|
2055
2159
|
}
|
|
2056
2160
|
});
|
|
2057
2161
|
}
|
|
2058
|
-
return submitResponse.request_id;
|
|
2059
2162
|
}
|
|
2060
|
-
async function pollSealOutputStatus(thresholdNetworkUrl, requestId) {
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2163
|
+
async function pollSealOutputStatus(thresholdNetworkUrl, requestId, overallStartTime, onPoll) {
|
|
2164
|
+
let attemptIndex = 0;
|
|
2165
|
+
while (true) {
|
|
2166
|
+
const elapsedMs = Date.now() - overallStartTime;
|
|
2167
|
+
const intervalMs = computeMinuteRampPollIntervalMs(elapsedMs, {
|
|
2168
|
+
minIntervalMs: POLL_INTERVAL_MS,
|
|
2169
|
+
maxIntervalMs: POLL_MAX_INTERVAL_MS
|
|
2170
|
+
});
|
|
2171
|
+
onPoll?.({
|
|
2172
|
+
operation: "sealoutput",
|
|
2173
|
+
requestId,
|
|
2174
|
+
attemptIndex,
|
|
2175
|
+
elapsedMs,
|
|
2176
|
+
intervalMs,
|
|
2177
|
+
timeoutMs: SEAL_OUTPUT_TIMEOUT_MS
|
|
2178
|
+
});
|
|
2179
|
+
if (elapsedMs > SEAL_OUTPUT_TIMEOUT_MS) {
|
|
2065
2180
|
throw new CofheError({
|
|
2066
2181
|
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
2067
|
-
message: `sealOutput polling timed out after ${
|
|
2182
|
+
message: `sealOutput polling timed out after ${SEAL_OUTPUT_TIMEOUT_MS}ms`,
|
|
2068
2183
|
hint: "The request may still be processing. Try again later.",
|
|
2069
2184
|
context: {
|
|
2070
2185
|
thresholdNetworkUrl,
|
|
2071
2186
|
requestId,
|
|
2072
|
-
timeoutMs:
|
|
2187
|
+
timeoutMs: SEAL_OUTPUT_TIMEOUT_MS
|
|
2073
2188
|
}
|
|
2074
2189
|
});
|
|
2075
2190
|
}
|
|
@@ -2138,32 +2253,14 @@ async function pollSealOutputStatus(thresholdNetworkUrl, requestId) {
|
|
|
2138
2253
|
});
|
|
2139
2254
|
}
|
|
2140
2255
|
if (statusResponse.status === "COMPLETED") {
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
context: {
|
|
2147
|
-
thresholdNetworkUrl,
|
|
2148
|
-
requestId,
|
|
2149
|
-
statusResponse
|
|
2150
|
-
}
|
|
2151
|
-
});
|
|
2152
|
-
}
|
|
2153
|
-
if (!statusResponse.sealed) {
|
|
2154
|
-
throw new CofheError({
|
|
2155
|
-
code: "SEAL_OUTPUT_RETURNED_NULL" /* SealOutputReturnedNull */,
|
|
2156
|
-
message: `sealOutput request completed but returned no sealed data`,
|
|
2157
|
-
context: {
|
|
2158
|
-
thresholdNetworkUrl,
|
|
2159
|
-
requestId,
|
|
2160
|
-
statusResponse
|
|
2161
|
-
}
|
|
2162
|
-
});
|
|
2163
|
-
}
|
|
2164
|
-
return convertSealedData(statusResponse.sealed);
|
|
2256
|
+
return parseCompletedSealOutputResponse({
|
|
2257
|
+
value: statusResponse,
|
|
2258
|
+
thresholdNetworkUrl,
|
|
2259
|
+
requestId
|
|
2260
|
+
});
|
|
2165
2261
|
}
|
|
2166
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
2262
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
2263
|
+
attemptIndex += 1;
|
|
2167
2264
|
}
|
|
2168
2265
|
throw new CofheError({
|
|
2169
2266
|
code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
|
|
@@ -2174,9 +2271,21 @@ async function pollSealOutputStatus(thresholdNetworkUrl, requestId) {
|
|
|
2174
2271
|
}
|
|
2175
2272
|
});
|
|
2176
2273
|
}
|
|
2177
|
-
async function tnSealOutputV2(
|
|
2178
|
-
const
|
|
2179
|
-
|
|
2274
|
+
async function tnSealOutputV2(params) {
|
|
2275
|
+
const { thresholdNetworkUrl, ctHash, chainId, permission, onPoll } = params;
|
|
2276
|
+
const overallStartTime = Date.now();
|
|
2277
|
+
const submitResult = await submitSealOutputRequest(
|
|
2278
|
+
thresholdNetworkUrl,
|
|
2279
|
+
ctHash,
|
|
2280
|
+
chainId,
|
|
2281
|
+
permission,
|
|
2282
|
+
overallStartTime,
|
|
2283
|
+
onPoll
|
|
2284
|
+
);
|
|
2285
|
+
if (submitResult.kind === "completed") {
|
|
2286
|
+
return submitResult.sealed;
|
|
2287
|
+
}
|
|
2288
|
+
return await pollSealOutputStatus(thresholdNetworkUrl, submitResult.requestId, overallStartTime, onPoll);
|
|
2180
2289
|
}
|
|
2181
2290
|
|
|
2182
2291
|
// core/decrypt/decryptForViewBuilder.ts
|
|
@@ -2185,6 +2294,7 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
2185
2294
|
utype;
|
|
2186
2295
|
permitHash;
|
|
2187
2296
|
permit;
|
|
2297
|
+
pollCallback;
|
|
2188
2298
|
constructor(params) {
|
|
2189
2299
|
super({
|
|
2190
2300
|
config: params.config,
|
|
@@ -2241,6 +2351,10 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
2241
2351
|
getAccount() {
|
|
2242
2352
|
return this.account;
|
|
2243
2353
|
}
|
|
2354
|
+
onPoll(callback) {
|
|
2355
|
+
this.pollCallback = callback;
|
|
2356
|
+
return this;
|
|
2357
|
+
}
|
|
2244
2358
|
withPermit(permitOrPermitHash) {
|
|
2245
2359
|
if (typeof permitOrPermitHash === "string") {
|
|
2246
2360
|
this.permitHash = permitOrPermitHash;
|
|
@@ -2364,7 +2478,13 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
2364
2478
|
this.assertPublicClient();
|
|
2365
2479
|
const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
|
|
2366
2480
|
const permission = PermitUtils.getPermission(permit, true);
|
|
2367
|
-
const sealed = await tnSealOutputV2(
|
|
2481
|
+
const sealed = await tnSealOutputV2({
|
|
2482
|
+
ctHash: this.ctHash,
|
|
2483
|
+
chainId: this.chainId,
|
|
2484
|
+
permission,
|
|
2485
|
+
thresholdNetworkUrl,
|
|
2486
|
+
onPoll: this.pollCallback
|
|
2487
|
+
});
|
|
2368
2488
|
return PermitUtils.unseal(permit, sealed);
|
|
2369
2489
|
}
|
|
2370
2490
|
/**
|
|
@@ -2392,7 +2512,6 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
2392
2512
|
this.validateUtypeOrThrow();
|
|
2393
2513
|
const permit = await this.getResolvedPermit();
|
|
2394
2514
|
PermitUtils.validate(permit);
|
|
2395
|
-
PermitUtils.isValid(permit);
|
|
2396
2515
|
const chainId = permit._signedDomain.chainId;
|
|
2397
2516
|
let unsealed;
|
|
2398
2517
|
if (chainId === hardhat$1.id) {
|
|
@@ -2403,6 +2522,9 @@ var DecryptForViewBuilder = class extends BaseBuilder {
|
|
|
2403
2522
|
return convertViaUtype(this.utype, unsealed);
|
|
2404
2523
|
}
|
|
2405
2524
|
};
|
|
2525
|
+
var UINT_TYPE_MASK = 0x7fn;
|
|
2526
|
+
var TYPE_BYTE_OFFSET = 8n;
|
|
2527
|
+
var getEncryptionTypeFromCtHash = (ctHash) => Number(ctHash >> TYPE_BYTE_OFFSET & UINT_TYPE_MASK);
|
|
2406
2528
|
async function cofheMocksDecryptForTx(ctHash, utype, permit, publicClient) {
|
|
2407
2529
|
let allowed;
|
|
2408
2530
|
let error;
|
|
@@ -2440,7 +2562,13 @@ async function cofheMocksDecryptForTx(ctHash, utype, permit, publicClient) {
|
|
|
2440
2562
|
message: `mocks decryptForTx call failed: ACL Access Denied (NotAllowed)`
|
|
2441
2563
|
});
|
|
2442
2564
|
}
|
|
2443
|
-
const
|
|
2565
|
+
const chainId = publicClient.chain?.id ?? await publicClient.getChainId();
|
|
2566
|
+
const normalizedCtHash = BigInt(ctHash);
|
|
2567
|
+
const encryptionType = getEncryptionTypeFromCtHash(normalizedCtHash);
|
|
2568
|
+
const packed = encodePacked(
|
|
2569
|
+
["uint256", "uint32", "uint64", "uint256"],
|
|
2570
|
+
[decryptedValue, encryptionType, BigInt(chainId), normalizedCtHash]
|
|
2571
|
+
);
|
|
2444
2572
|
const messageHash = keccak256(packed);
|
|
2445
2573
|
const signature = await sign({
|
|
2446
2574
|
hash: messageHash,
|
|
@@ -2453,7 +2581,7 @@ async function cofheMocksDecryptForTx(ctHash, utype, permit, publicClient) {
|
|
|
2453
2581
|
signature
|
|
2454
2582
|
};
|
|
2455
2583
|
}
|
|
2456
|
-
function
|
|
2584
|
+
function normalizeTnSignature(signature) {
|
|
2457
2585
|
if (typeof signature !== "string") {
|
|
2458
2586
|
throw new CofheError({
|
|
2459
2587
|
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
@@ -2509,141 +2637,384 @@ function parseDecryptedBytesToBigInt(decrypted) {
|
|
|
2509
2637
|
}
|
|
2510
2638
|
return BigInt(`0x${hex}`);
|
|
2511
2639
|
}
|
|
2512
|
-
|
|
2640
|
+
|
|
2641
|
+
// core/decrypt/tnDecryptV2.ts
|
|
2642
|
+
var POLL_INTERVAL_MS2 = 1e3;
|
|
2643
|
+
var POLL_MAX_INTERVAL_MS2 = 1e4;
|
|
2644
|
+
var DECRYPT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
2645
|
+
var SUBMIT_RETRY_INTERVAL_MS2 = 1e3;
|
|
2646
|
+
function assertDecryptSubmitResponseV2(value) {
|
|
2513
2647
|
if (value == null || typeof value !== "object") {
|
|
2514
2648
|
throw new CofheError({
|
|
2515
2649
|
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2516
|
-
message: "decrypt response must be a JSON object",
|
|
2650
|
+
message: "decrypt submit response must be a JSON object",
|
|
2517
2651
|
context: {
|
|
2518
2652
|
value
|
|
2519
2653
|
}
|
|
2520
2654
|
});
|
|
2521
2655
|
}
|
|
2522
2656
|
const v = value;
|
|
2523
|
-
|
|
2524
|
-
const signature = v.signature;
|
|
2525
|
-
const encryptionType = v.encryption_type;
|
|
2526
|
-
const errorMessage = v.error_message;
|
|
2527
|
-
if (!Array.isArray(decrypted)) {
|
|
2657
|
+
if (v.request_id !== null && typeof v.request_id !== "string") {
|
|
2528
2658
|
throw new CofheError({
|
|
2529
|
-
code: "
|
|
2530
|
-
message: "decrypt response
|
|
2531
|
-
context: {
|
|
2659
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2660
|
+
message: "decrypt submit response has invalid request_id",
|
|
2661
|
+
context: {
|
|
2662
|
+
value
|
|
2663
|
+
}
|
|
2532
2664
|
});
|
|
2533
2665
|
}
|
|
2534
|
-
|
|
2666
|
+
return {
|
|
2667
|
+
request_id: v.request_id ?? null,
|
|
2668
|
+
status: typeof v.status === "string" ? v.status : void 0,
|
|
2669
|
+
is_succeed: typeof v.is_succeed === "boolean" ? v.is_succeed : void 0,
|
|
2670
|
+
decrypted: Array.isArray(v.decrypted) ? v.decrypted : void 0,
|
|
2671
|
+
signature: typeof v.signature === "string" ? v.signature : void 0,
|
|
2672
|
+
encryption_type: typeof v.encryption_type === "number" ? v.encryption_type : void 0,
|
|
2673
|
+
error_message: typeof v.error_message === "string" || v.error_message === null ? v.error_message : void 0,
|
|
2674
|
+
message: typeof v.message === "string" ? v.message : void 0
|
|
2675
|
+
};
|
|
2676
|
+
}
|
|
2677
|
+
function parseCompletedDecryptResponseV2(params) {
|
|
2678
|
+
const { value, thresholdNetworkUrl, requestId } = params;
|
|
2679
|
+
if (value.is_succeed === false) {
|
|
2680
|
+
const errorMessage = value.error_message || "Unknown error";
|
|
2535
2681
|
throw new CofheError({
|
|
2536
|
-
code: "
|
|
2537
|
-
message:
|
|
2538
|
-
context: {
|
|
2682
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2683
|
+
message: `decrypt request failed: ${errorMessage}`,
|
|
2684
|
+
context: {
|
|
2685
|
+
thresholdNetworkUrl,
|
|
2686
|
+
requestId,
|
|
2687
|
+
response: value
|
|
2688
|
+
}
|
|
2539
2689
|
});
|
|
2540
2690
|
}
|
|
2541
|
-
if (
|
|
2691
|
+
if (value.error_message) {
|
|
2542
2692
|
throw new CofheError({
|
|
2543
2693
|
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2544
|
-
message:
|
|
2545
|
-
context: {
|
|
2694
|
+
message: `decrypt request failed: ${value.error_message}`,
|
|
2695
|
+
context: {
|
|
2696
|
+
thresholdNetworkUrl,
|
|
2697
|
+
requestId,
|
|
2698
|
+
response: value
|
|
2699
|
+
}
|
|
2546
2700
|
});
|
|
2547
2701
|
}
|
|
2548
|
-
if (!(
|
|
2702
|
+
if (!Array.isArray(value.decrypted)) {
|
|
2549
2703
|
throw new CofheError({
|
|
2550
|
-
code: "
|
|
2551
|
-
message: "decrypt response
|
|
2552
|
-
context: {
|
|
2704
|
+
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
2705
|
+
message: "decrypt completed but response missing <decrypted> byte array",
|
|
2706
|
+
context: {
|
|
2707
|
+
thresholdNetworkUrl,
|
|
2708
|
+
requestId,
|
|
2709
|
+
response: value
|
|
2710
|
+
}
|
|
2553
2711
|
});
|
|
2554
2712
|
}
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
encryption_type: encryptionType,
|
|
2559
|
-
error_message: errorMessage
|
|
2560
|
-
};
|
|
2713
|
+
const decryptedValue = parseDecryptedBytesToBigInt(value.decrypted);
|
|
2714
|
+
const signature = normalizeTnSignature(value.signature);
|
|
2715
|
+
return { decryptedValue, signature };
|
|
2561
2716
|
}
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
let response;
|
|
2571
|
-
try {
|
|
2572
|
-
response = await fetch(`${thresholdNetworkUrl}/decrypt`, {
|
|
2573
|
-
method: "POST",
|
|
2574
|
-
headers: {
|
|
2575
|
-
"Content-Type": "application/json"
|
|
2576
|
-
},
|
|
2577
|
-
body: JSON.stringify(body)
|
|
2717
|
+
function assertDecryptStatusResponseV2(value) {
|
|
2718
|
+
if (value == null || typeof value !== "object") {
|
|
2719
|
+
throw new CofheError({
|
|
2720
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2721
|
+
message: "decrypt status response must be a JSON object",
|
|
2722
|
+
context: {
|
|
2723
|
+
value
|
|
2724
|
+
}
|
|
2578
2725
|
});
|
|
2579
|
-
}
|
|
2726
|
+
}
|
|
2727
|
+
const v = value;
|
|
2728
|
+
const requestId = v.request_id;
|
|
2729
|
+
const status = v.status;
|
|
2730
|
+
const submittedAt = v.submitted_at;
|
|
2731
|
+
if (typeof requestId !== "string" || requestId.trim().length === 0) {
|
|
2580
2732
|
throw new CofheError({
|
|
2581
2733
|
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2582
|
-
message:
|
|
2583
|
-
hint: "Ensure the threshold network URL is valid and reachable.",
|
|
2584
|
-
cause: e instanceof Error ? e : void 0,
|
|
2734
|
+
message: "decrypt status response missing request_id",
|
|
2585
2735
|
context: {
|
|
2586
|
-
|
|
2587
|
-
body
|
|
2736
|
+
value
|
|
2588
2737
|
}
|
|
2589
2738
|
});
|
|
2590
2739
|
}
|
|
2591
|
-
|
|
2592
|
-
if (!response.ok) {
|
|
2593
|
-
let errorMessage = response.statusText || `HTTP ${response.status}`;
|
|
2594
|
-
try {
|
|
2595
|
-
const errorBody = JSON.parse(responseText);
|
|
2596
|
-
const maybeMessage = errorBody.error_message || errorBody.message;
|
|
2597
|
-
if (typeof maybeMessage === "string" && maybeMessage.length > 0)
|
|
2598
|
-
errorMessage = maybeMessage;
|
|
2599
|
-
} catch {
|
|
2600
|
-
const trimmed = responseText.trim();
|
|
2601
|
-
if (trimmed.length > 0)
|
|
2602
|
-
errorMessage = trimmed;
|
|
2603
|
-
}
|
|
2740
|
+
if (status !== "PROCESSING" && status !== "COMPLETED") {
|
|
2604
2741
|
throw new CofheError({
|
|
2605
2742
|
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2606
|
-
message:
|
|
2607
|
-
hint: "Check the threshold network URL and request parameters.",
|
|
2743
|
+
message: "decrypt status response has invalid status",
|
|
2608
2744
|
context: {
|
|
2609
|
-
|
|
2610
|
-
status
|
|
2611
|
-
statusText: response.statusText,
|
|
2612
|
-
body,
|
|
2613
|
-
responseText
|
|
2745
|
+
value,
|
|
2746
|
+
status
|
|
2614
2747
|
}
|
|
2615
2748
|
});
|
|
2616
2749
|
}
|
|
2617
|
-
|
|
2618
|
-
try {
|
|
2619
|
-
rawJson = JSON.parse(responseText);
|
|
2620
|
-
} catch (e) {
|
|
2750
|
+
if (typeof submittedAt !== "string" || submittedAt.trim().length === 0) {
|
|
2621
2751
|
throw new CofheError({
|
|
2622
2752
|
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2623
|
-
message:
|
|
2624
|
-
cause: e instanceof Error ? e : void 0,
|
|
2753
|
+
message: "decrypt status response missing submitted_at",
|
|
2625
2754
|
context: {
|
|
2626
|
-
|
|
2627
|
-
body,
|
|
2628
|
-
responseText
|
|
2755
|
+
value
|
|
2629
2756
|
}
|
|
2630
2757
|
});
|
|
2631
2758
|
}
|
|
2632
|
-
|
|
2633
|
-
|
|
2759
|
+
return value;
|
|
2760
|
+
}
|
|
2761
|
+
async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, permission, overallStartTime, onPoll) {
|
|
2762
|
+
const body = {
|
|
2763
|
+
ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
|
|
2764
|
+
host_chain_id: chainId
|
|
2765
|
+
};
|
|
2766
|
+
if (permission) {
|
|
2767
|
+
body.permit = permission;
|
|
2768
|
+
}
|
|
2769
|
+
let attemptIndex = 0;
|
|
2770
|
+
for (; ; ) {
|
|
2771
|
+
let response;
|
|
2772
|
+
try {
|
|
2773
|
+
response = await fetch(`${thresholdNetworkUrl}/v2/decrypt`, {
|
|
2774
|
+
method: "POST",
|
|
2775
|
+
headers: {
|
|
2776
|
+
"Content-Type": "application/json"
|
|
2777
|
+
},
|
|
2778
|
+
body: JSON.stringify(body)
|
|
2779
|
+
});
|
|
2780
|
+
} catch (e) {
|
|
2781
|
+
throw new CofheError({
|
|
2782
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2783
|
+
message: `decrypt request failed`,
|
|
2784
|
+
hint: "Ensure the threshold network URL is valid and reachable.",
|
|
2785
|
+
cause: e instanceof Error ? e : void 0,
|
|
2786
|
+
context: {
|
|
2787
|
+
thresholdNetworkUrl,
|
|
2788
|
+
body,
|
|
2789
|
+
attemptIndex
|
|
2790
|
+
}
|
|
2791
|
+
});
|
|
2792
|
+
}
|
|
2793
|
+
if (!response.ok) {
|
|
2794
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
2795
|
+
try {
|
|
2796
|
+
const errorBody = await response.json();
|
|
2797
|
+
const maybeMessage = errorBody.error_message || errorBody.message;
|
|
2798
|
+
if (typeof maybeMessage === "string" && maybeMessage.length > 0)
|
|
2799
|
+
errorMessage = maybeMessage;
|
|
2800
|
+
} catch {
|
|
2801
|
+
errorMessage = response.statusText || errorMessage;
|
|
2802
|
+
}
|
|
2803
|
+
throw new CofheError({
|
|
2804
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2805
|
+
message: `decrypt request failed: ${errorMessage}`,
|
|
2806
|
+
hint: "Check the threshold network URL and request parameters.",
|
|
2807
|
+
context: {
|
|
2808
|
+
thresholdNetworkUrl,
|
|
2809
|
+
status: response.status,
|
|
2810
|
+
statusText: response.statusText,
|
|
2811
|
+
body,
|
|
2812
|
+
attemptIndex
|
|
2813
|
+
}
|
|
2814
|
+
});
|
|
2815
|
+
}
|
|
2816
|
+
let submitResponse;
|
|
2817
|
+
if (response.status !== 204) {
|
|
2818
|
+
let rawJson;
|
|
2819
|
+
try {
|
|
2820
|
+
rawJson = await response.json();
|
|
2821
|
+
} catch (e) {
|
|
2822
|
+
throw new CofheError({
|
|
2823
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2824
|
+
message: `Failed to parse decrypt submit response`,
|
|
2825
|
+
cause: e instanceof Error ? e : void 0,
|
|
2826
|
+
context: {
|
|
2827
|
+
thresholdNetworkUrl,
|
|
2828
|
+
body,
|
|
2829
|
+
attemptIndex
|
|
2830
|
+
}
|
|
2831
|
+
});
|
|
2832
|
+
}
|
|
2833
|
+
submitResponse = assertDecryptSubmitResponseV2(rawJson);
|
|
2834
|
+
if (Array.isArray(submitResponse.decrypted) && typeof submitResponse.signature === "string") {
|
|
2835
|
+
return {
|
|
2836
|
+
kind: "completed",
|
|
2837
|
+
...parseCompletedDecryptResponseV2({
|
|
2838
|
+
value: submitResponse,
|
|
2839
|
+
thresholdNetworkUrl,
|
|
2840
|
+
requestId: submitResponse.request_id
|
|
2841
|
+
})
|
|
2842
|
+
};
|
|
2843
|
+
}
|
|
2844
|
+
if (submitResponse.request_id) {
|
|
2845
|
+
return { kind: "request_id", requestId: submitResponse.request_id };
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
if (response.status === 204) {
|
|
2849
|
+
const elapsedMs = Date.now() - overallStartTime;
|
|
2850
|
+
if (elapsedMs > DECRYPT_TIMEOUT_MS) {
|
|
2851
|
+
throw new CofheError({
|
|
2852
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2853
|
+
message: `decrypt submit retried without receiving request_id for ${DECRYPT_TIMEOUT_MS}ms`,
|
|
2854
|
+
hint: "The ciphertext may still be propagating. Try again later.",
|
|
2855
|
+
context: {
|
|
2856
|
+
thresholdNetworkUrl,
|
|
2857
|
+
body,
|
|
2858
|
+
attemptIndex,
|
|
2859
|
+
timeoutMs: DECRYPT_TIMEOUT_MS,
|
|
2860
|
+
submitResponse,
|
|
2861
|
+
status: response.status
|
|
2862
|
+
}
|
|
2863
|
+
});
|
|
2864
|
+
}
|
|
2865
|
+
onPoll?.({
|
|
2866
|
+
operation: "decrypt",
|
|
2867
|
+
requestId: "",
|
|
2868
|
+
attemptIndex,
|
|
2869
|
+
elapsedMs,
|
|
2870
|
+
intervalMs: SUBMIT_RETRY_INTERVAL_MS2,
|
|
2871
|
+
timeoutMs: DECRYPT_TIMEOUT_MS
|
|
2872
|
+
});
|
|
2873
|
+
await new Promise((resolve) => setTimeout(resolve, SUBMIT_RETRY_INTERVAL_MS2));
|
|
2874
|
+
attemptIndex += 1;
|
|
2875
|
+
continue;
|
|
2876
|
+
}
|
|
2634
2877
|
throw new CofheError({
|
|
2635
2878
|
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2636
|
-
message: `decrypt
|
|
2879
|
+
message: `decrypt submit response missing request_id`,
|
|
2637
2880
|
context: {
|
|
2638
2881
|
thresholdNetworkUrl,
|
|
2639
2882
|
body,
|
|
2640
|
-
|
|
2883
|
+
submitResponse,
|
|
2884
|
+
attemptIndex
|
|
2641
2885
|
}
|
|
2642
2886
|
});
|
|
2643
2887
|
}
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2888
|
+
}
|
|
2889
|
+
async function pollDecryptStatusV2(thresholdNetworkUrl, requestId, overallStartTime, onPoll) {
|
|
2890
|
+
let attemptIndex = 0;
|
|
2891
|
+
while (true) {
|
|
2892
|
+
const elapsedMs = Date.now() - overallStartTime;
|
|
2893
|
+
const intervalMs = computeMinuteRampPollIntervalMs(elapsedMs, {
|
|
2894
|
+
minIntervalMs: POLL_INTERVAL_MS2,
|
|
2895
|
+
maxIntervalMs: POLL_MAX_INTERVAL_MS2
|
|
2896
|
+
});
|
|
2897
|
+
onPoll?.({
|
|
2898
|
+
operation: "decrypt",
|
|
2899
|
+
requestId,
|
|
2900
|
+
attemptIndex,
|
|
2901
|
+
elapsedMs,
|
|
2902
|
+
intervalMs,
|
|
2903
|
+
timeoutMs: DECRYPT_TIMEOUT_MS
|
|
2904
|
+
});
|
|
2905
|
+
if (elapsedMs > DECRYPT_TIMEOUT_MS) {
|
|
2906
|
+
throw new CofheError({
|
|
2907
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2908
|
+
message: `decrypt polling timed out after ${DECRYPT_TIMEOUT_MS}ms`,
|
|
2909
|
+
hint: "The request may still be processing. Try again later.",
|
|
2910
|
+
context: {
|
|
2911
|
+
thresholdNetworkUrl,
|
|
2912
|
+
requestId,
|
|
2913
|
+
timeoutMs: DECRYPT_TIMEOUT_MS
|
|
2914
|
+
}
|
|
2915
|
+
});
|
|
2916
|
+
}
|
|
2917
|
+
let response;
|
|
2918
|
+
try {
|
|
2919
|
+
response = await fetch(`${thresholdNetworkUrl}/v2/decrypt/${requestId}`, {
|
|
2920
|
+
method: "GET",
|
|
2921
|
+
headers: {
|
|
2922
|
+
"Content-Type": "application/json"
|
|
2923
|
+
}
|
|
2924
|
+
});
|
|
2925
|
+
} catch (e) {
|
|
2926
|
+
throw new CofheError({
|
|
2927
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2928
|
+
message: `decrypt status poll failed`,
|
|
2929
|
+
hint: "Ensure the threshold network URL is valid and reachable.",
|
|
2930
|
+
cause: e instanceof Error ? e : void 0,
|
|
2931
|
+
context: {
|
|
2932
|
+
thresholdNetworkUrl,
|
|
2933
|
+
requestId
|
|
2934
|
+
}
|
|
2935
|
+
});
|
|
2936
|
+
}
|
|
2937
|
+
if (response.status === 404) {
|
|
2938
|
+
throw new CofheError({
|
|
2939
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2940
|
+
message: `decrypt request not found: ${requestId}`,
|
|
2941
|
+
hint: "The request may have expired or been invalid.",
|
|
2942
|
+
context: {
|
|
2943
|
+
thresholdNetworkUrl,
|
|
2944
|
+
requestId
|
|
2945
|
+
}
|
|
2946
|
+
});
|
|
2947
|
+
}
|
|
2948
|
+
if (!response.ok) {
|
|
2949
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
2950
|
+
try {
|
|
2951
|
+
const errorBody = await response.json();
|
|
2952
|
+
const maybeMessage = errorBody.error_message || errorBody.message;
|
|
2953
|
+
if (typeof maybeMessage === "string" && maybeMessage.length > 0)
|
|
2954
|
+
errorMessage = maybeMessage;
|
|
2955
|
+
} catch {
|
|
2956
|
+
errorMessage = response.statusText || errorMessage;
|
|
2957
|
+
}
|
|
2958
|
+
throw new CofheError({
|
|
2959
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2960
|
+
message: `decrypt status poll failed: ${errorMessage}`,
|
|
2961
|
+
context: {
|
|
2962
|
+
thresholdNetworkUrl,
|
|
2963
|
+
requestId,
|
|
2964
|
+
status: response.status,
|
|
2965
|
+
statusText: response.statusText
|
|
2966
|
+
}
|
|
2967
|
+
});
|
|
2968
|
+
}
|
|
2969
|
+
let rawJson;
|
|
2970
|
+
try {
|
|
2971
|
+
rawJson = await response.json();
|
|
2972
|
+
} catch (e) {
|
|
2973
|
+
throw new CofheError({
|
|
2974
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2975
|
+
message: `Failed to parse decrypt status response`,
|
|
2976
|
+
cause: e instanceof Error ? e : void 0,
|
|
2977
|
+
context: {
|
|
2978
|
+
thresholdNetworkUrl,
|
|
2979
|
+
requestId
|
|
2980
|
+
}
|
|
2981
|
+
});
|
|
2982
|
+
}
|
|
2983
|
+
const statusResponse = assertDecryptStatusResponseV2(rawJson);
|
|
2984
|
+
if (statusResponse.status === "COMPLETED") {
|
|
2985
|
+
return parseCompletedDecryptResponseV2({
|
|
2986
|
+
value: statusResponse,
|
|
2987
|
+
thresholdNetworkUrl,
|
|
2988
|
+
requestId
|
|
2989
|
+
});
|
|
2990
|
+
}
|
|
2991
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
2992
|
+
attemptIndex += 1;
|
|
2993
|
+
}
|
|
2994
|
+
throw new CofheError({
|
|
2995
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2996
|
+
message: "Polling loop exited unexpectedly",
|
|
2997
|
+
context: {
|
|
2998
|
+
thresholdNetworkUrl,
|
|
2999
|
+
requestId
|
|
3000
|
+
}
|
|
3001
|
+
});
|
|
3002
|
+
}
|
|
3003
|
+
async function tnDecryptV2(params) {
|
|
3004
|
+
const { thresholdNetworkUrl, ctHash, chainId, permission, onPoll } = params;
|
|
3005
|
+
const overallStartTime = Date.now();
|
|
3006
|
+
const submitResult = await submitDecryptRequestV2(
|
|
3007
|
+
thresholdNetworkUrl,
|
|
3008
|
+
ctHash,
|
|
3009
|
+
chainId,
|
|
3010
|
+
permission,
|
|
3011
|
+
overallStartTime,
|
|
3012
|
+
onPoll
|
|
3013
|
+
);
|
|
3014
|
+
if (submitResult.kind === "completed") {
|
|
3015
|
+
return submitResult;
|
|
3016
|
+
}
|
|
3017
|
+
return await pollDecryptStatusV2(thresholdNetworkUrl, submitResult.requestId, overallStartTime, onPoll);
|
|
2647
3018
|
}
|
|
2648
3019
|
|
|
2649
3020
|
// core/decrypt/decryptForTxBuilder.ts
|
|
@@ -2652,6 +3023,7 @@ var DecryptForTxBuilder = class extends BaseBuilder {
|
|
|
2652
3023
|
permitHash;
|
|
2653
3024
|
permit;
|
|
2654
3025
|
permitSelection = "unset";
|
|
3026
|
+
pollCallback;
|
|
2655
3027
|
constructor(params) {
|
|
2656
3028
|
super({
|
|
2657
3029
|
config: params.config,
|
|
@@ -2677,6 +3049,10 @@ var DecryptForTxBuilder = class extends BaseBuilder {
|
|
|
2677
3049
|
getAccount() {
|
|
2678
3050
|
return this.account;
|
|
2679
3051
|
}
|
|
3052
|
+
onPoll(callback) {
|
|
3053
|
+
this.pollCallback = callback;
|
|
3054
|
+
return this;
|
|
3055
|
+
}
|
|
2680
3056
|
withPermit(permitOrPermitHash) {
|
|
2681
3057
|
if (this.permitSelection === "with-permit") {
|
|
2682
3058
|
throw new CofheError({
|
|
@@ -2804,7 +3180,13 @@ var DecryptForTxBuilder = class extends BaseBuilder {
|
|
|
2804
3180
|
this.assertPublicClient();
|
|
2805
3181
|
const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
|
|
2806
3182
|
const permission = permit ? PermitUtils.getPermission(permit, true) : null;
|
|
2807
|
-
const { decryptedValue, signature } = await
|
|
3183
|
+
const { decryptedValue, signature } = await tnDecryptV2({
|
|
3184
|
+
ctHash: this.ctHash,
|
|
3185
|
+
chainId: this.chainId,
|
|
3186
|
+
permission,
|
|
3187
|
+
thresholdNetworkUrl,
|
|
3188
|
+
onPoll: this.pollCallback
|
|
3189
|
+
});
|
|
2808
3190
|
return {
|
|
2809
3191
|
ctHash: this.ctHash,
|
|
2810
3192
|
decryptedValue,
|
|
@@ -2822,7 +3204,6 @@ var DecryptForTxBuilder = class extends BaseBuilder {
|
|
|
2822
3204
|
const permit = await this.getResolvedPermit();
|
|
2823
3205
|
if (permit !== null) {
|
|
2824
3206
|
PermitUtils.validate(permit);
|
|
2825
|
-
PermitUtils.isValid(permit);
|
|
2826
3207
|
const chainId = permit._signedDomain.chainId;
|
|
2827
3208
|
if (chainId === hardhat$1.id) {
|
|
2828
3209
|
return await this.mocksDecryptForTx(permit);
|
|
@@ -2843,6 +3224,35 @@ var DecryptForTxBuilder = class extends BaseBuilder {
|
|
|
2843
3224
|
}
|
|
2844
3225
|
}
|
|
2845
3226
|
};
|
|
3227
|
+
var decryptResultSignerAbi = parseAbi(["function decryptResultSigner() view returns (address)"]);
|
|
3228
|
+
var UINT_TYPE_MASK2 = 0x7fn;
|
|
3229
|
+
var TYPE_BYTE_OFFSET2 = 8n;
|
|
3230
|
+
var getEncryptionTypeFromCtHash2 = (ctHash) => Number(ctHash >> TYPE_BYTE_OFFSET2 & UINT_TYPE_MASK2);
|
|
3231
|
+
var buildDecryptResultHash = (ctHash, cleartext, chainId) => {
|
|
3232
|
+
const encryptionType = getEncryptionTypeFromCtHash2(ctHash);
|
|
3233
|
+
return keccak256(
|
|
3234
|
+
encodePacked(["uint256", "uint32", "uint64", "uint256"], [cleartext, encryptionType, BigInt(chainId), ctHash])
|
|
3235
|
+
);
|
|
3236
|
+
};
|
|
3237
|
+
async function verifyDecryptResult(handle, cleartext, signature, publicClient) {
|
|
3238
|
+
const chainId = publicClient.chain?.id ?? await publicClient.getChainId();
|
|
3239
|
+
const expectedSigner = await publicClient.readContract({
|
|
3240
|
+
address: TASK_MANAGER_ADDRESS,
|
|
3241
|
+
abi: decryptResultSignerAbi,
|
|
3242
|
+
functionName: "decryptResultSigner",
|
|
3243
|
+
args: []
|
|
3244
|
+
});
|
|
3245
|
+
if (isAddressEqual(expectedSigner, zeroAddress))
|
|
3246
|
+
return true;
|
|
3247
|
+
const ctHash = BigInt(handle);
|
|
3248
|
+
const messageHash = buildDecryptResultHash(ctHash, cleartext, chainId);
|
|
3249
|
+
try {
|
|
3250
|
+
const recovered = await recoverAddress({ hash: messageHash, signature });
|
|
3251
|
+
return isAddressEqual(recovered, expectedSigner);
|
|
3252
|
+
} catch {
|
|
3253
|
+
return false;
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
2846
3256
|
|
|
2847
3257
|
// core/client.ts
|
|
2848
3258
|
var InitialConnectStore = {
|
|
@@ -2961,6 +3371,11 @@ function createCofheClientBase(opts) {
|
|
|
2961
3371
|
requireConnected: _requireConnected
|
|
2962
3372
|
});
|
|
2963
3373
|
}
|
|
3374
|
+
function verifyDecryptResult2(handle, cleartext, signature) {
|
|
3375
|
+
_requireConnected();
|
|
3376
|
+
const { publicClient } = connectStore.getState();
|
|
3377
|
+
return verifyDecryptResult(handle, cleartext, signature, publicClient);
|
|
3378
|
+
}
|
|
2964
3379
|
const _getChainIdAndAccount = (chainId, account) => {
|
|
2965
3380
|
const state = connectStore.getState();
|
|
2966
3381
|
const _chainId = chainId ?? state.chainId;
|
|
@@ -3012,19 +3427,19 @@ function createCofheClientBase(opts) {
|
|
|
3012
3427
|
return permits.getOrCreateSharingPermit(publicClient, walletClient, options, _chainId, _account);
|
|
3013
3428
|
},
|
|
3014
3429
|
// Retrieval methods (auto-fill chainId/account)
|
|
3015
|
-
getPermit:
|
|
3430
|
+
getPermit: (hash, chainId, account) => {
|
|
3016
3431
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
3017
3432
|
return permits.getPermit(_chainId, _account, hash);
|
|
3018
3433
|
},
|
|
3019
|
-
getPermits:
|
|
3434
|
+
getPermits: (chainId, account) => {
|
|
3020
3435
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
3021
3436
|
return permits.getPermits(_chainId, _account);
|
|
3022
3437
|
},
|
|
3023
|
-
getActivePermit:
|
|
3438
|
+
getActivePermit: (chainId, account) => {
|
|
3024
3439
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
3025
3440
|
return permits.getActivePermit(_chainId, _account);
|
|
3026
3441
|
},
|
|
3027
|
-
getActivePermitHash:
|
|
3442
|
+
getActivePermitHash: (chainId, account) => {
|
|
3028
3443
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
3029
3444
|
return permits.getActivePermitHash(_chainId, _account);
|
|
3030
3445
|
},
|
|
@@ -3043,6 +3458,7 @@ function createCofheClientBase(opts) {
|
|
|
3043
3458
|
},
|
|
3044
3459
|
// Utils (no context needed)
|
|
3045
3460
|
getHash: permits.getHash,
|
|
3461
|
+
export: permits.export,
|
|
3046
3462
|
serialize: permits.serialize,
|
|
3047
3463
|
deserialize: permits.deserialize
|
|
3048
3464
|
};
|
|
@@ -3071,6 +3487,7 @@ function createCofheClientBase(opts) {
|
|
|
3071
3487
|
*/
|
|
3072
3488
|
decryptHandle: decryptForView,
|
|
3073
3489
|
decryptForTx,
|
|
3490
|
+
verifyDecryptResult: verifyDecryptResult2,
|
|
3074
3491
|
permits: clientPermits
|
|
3075
3492
|
// Add SDK-specific methods below that require connection
|
|
3076
3493
|
// Example:
|
|
@@ -3081,4 +3498,4 @@ function createCofheClientBase(opts) {
|
|
|
3081
3498
|
};
|
|
3082
3499
|
}
|
|
3083
3500
|
|
|
3084
|
-
export { CofheError, CofheErrorCode, DecryptForTxBuilder, DecryptForViewBuilder, EncryptInputsBuilder, EncryptStep, Encryptable, FheAllUTypes, FheTypes, FheUintUTypes, InitialConnectStore, assertCorrectEncryptedItemInput, createCofheClientBase, createCofheConfigBase, createKeysStore, fetchKeys, fheTypeToString, getCofheConfigItem, isCofheError, isEncryptableItem, isLastEncryptionStep, zkProveWithWorker };
|
|
3501
|
+
export { CofheError, CofheErrorCode, DecryptForTxBuilder, DecryptForViewBuilder, EncryptInputsBuilder, EncryptStep, Encryptable, FheAllUTypes, FheTypes, FheUintUTypes, InitialConnectStore, assertCorrectEncryptedItemInput, createCofheClientBase, createCofheConfigBase, createKeysStore, fetchKeys, fheTypeToString, getCofheConfigItem, isCofheError, isEncryptableItem, isLastEncryptionStep, verifyDecryptResult, zkProveWithWorker };
|