@cofhe/sdk 0.4.0 → 0.5.1
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 +11 -1
- package/core/clientTypes.ts +3 -1
- package/core/consts.ts +9 -0
- package/core/decrypt/cofheMocksDecryptForTx.ts +14 -3
- package/core/decrypt/decryptForTxBuilder.ts +16 -2
- package/core/decrypt/decryptForViewBuilder.ts +14 -7
- package/core/decrypt/polling.ts +14 -0
- package/core/decrypt/tnDecryptV2.ts +250 -110
- 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 +5 -0
- 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 +13 -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-MXND5SVN.js → chunk-S7OKGLFD.js} +485 -207
- package/dist/{clientTypes-kkrRdawm.d.ts → clientTypes-BSbwairE.d.cts} +23 -6
- package/dist/{clientTypes-ACVWbrXL.d.cts → clientTypes-DDmcgZ0a.d.ts} +23 -6
- package/dist/core.cjs +561 -244
- package/dist/core.d.cts +24 -6
- package/dist/core.d.ts +24 -6
- package/dist/core.js +3 -2
- package/dist/node.cjs +566 -246
- 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.cts → permit-DnVMDT5h.d.cts} +34 -4
- package/dist/{permit-MZ502UBl.d.ts → 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 +604 -256
- package/dist/web.d.cts +8 -4
- package/dist/web.d.ts +8 -4
- package/dist/web.js +49 -14
- package/dist/zkProve.worker.cjs +72 -64
- package/dist/zkProve.worker.js +71 -64
- 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 +40 -11
- 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 +94 -84
- 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,6 +1673,9 @@ 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
|
};
|
|
@@ -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,
|
|
@@ -2512,7 +2640,9 @@ function parseDecryptedBytesToBigInt(decrypted) {
|
|
|
2512
2640
|
|
|
2513
2641
|
// core/decrypt/tnDecryptV2.ts
|
|
2514
2642
|
var POLL_INTERVAL_MS2 = 1e3;
|
|
2515
|
-
var
|
|
2643
|
+
var POLL_MAX_INTERVAL_MS2 = 1e4;
|
|
2644
|
+
var DECRYPT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
2645
|
+
var SUBMIT_RETRY_INTERVAL_MS2 = 1e3;
|
|
2516
2646
|
function assertDecryptSubmitResponseV2(value) {
|
|
2517
2647
|
if (value == null || typeof value !== "object") {
|
|
2518
2648
|
throw new CofheError({
|
|
@@ -2524,16 +2654,65 @@ function assertDecryptSubmitResponseV2(value) {
|
|
|
2524
2654
|
});
|
|
2525
2655
|
}
|
|
2526
2656
|
const v = value;
|
|
2527
|
-
if (
|
|
2657
|
+
if (v.request_id !== null && typeof v.request_id !== "string") {
|
|
2528
2658
|
throw new CofheError({
|
|
2529
2659
|
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2530
|
-
message: "decrypt submit response
|
|
2660
|
+
message: "decrypt submit response has invalid request_id",
|
|
2531
2661
|
context: {
|
|
2532
2662
|
value
|
|
2533
2663
|
}
|
|
2534
2664
|
});
|
|
2535
2665
|
}
|
|
2536
|
-
return {
|
|
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";
|
|
2681
|
+
throw new CofheError({
|
|
2682
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2683
|
+
message: `decrypt request failed: ${errorMessage}`,
|
|
2684
|
+
context: {
|
|
2685
|
+
thresholdNetworkUrl,
|
|
2686
|
+
requestId,
|
|
2687
|
+
response: value
|
|
2688
|
+
}
|
|
2689
|
+
});
|
|
2690
|
+
}
|
|
2691
|
+
if (value.error_message) {
|
|
2692
|
+
throw new CofheError({
|
|
2693
|
+
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2694
|
+
message: `decrypt request failed: ${value.error_message}`,
|
|
2695
|
+
context: {
|
|
2696
|
+
thresholdNetworkUrl,
|
|
2697
|
+
requestId,
|
|
2698
|
+
response: value
|
|
2699
|
+
}
|
|
2700
|
+
});
|
|
2701
|
+
}
|
|
2702
|
+
if (!Array.isArray(value.decrypted)) {
|
|
2703
|
+
throw new CofheError({
|
|
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
|
+
}
|
|
2711
|
+
});
|
|
2712
|
+
}
|
|
2713
|
+
const decryptedValue = parseDecryptedBytesToBigInt(value.decrypted);
|
|
2714
|
+
const signature = normalizeTnSignature(value.signature);
|
|
2715
|
+
return { decryptedValue, signature };
|
|
2537
2716
|
}
|
|
2538
2717
|
function assertDecryptStatusResponseV2(value) {
|
|
2539
2718
|
if (value == null || typeof value !== "object") {
|
|
@@ -2579,7 +2758,7 @@ function assertDecryptStatusResponseV2(value) {
|
|
|
2579
2758
|
}
|
|
2580
2759
|
return value;
|
|
2581
2760
|
}
|
|
2582
|
-
async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, permission) {
|
|
2761
|
+
async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, permission, overallStartTime, onPoll) {
|
|
2583
2762
|
const body = {
|
|
2584
2763
|
ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
|
|
2585
2764
|
host_chain_id: chainId
|
|
@@ -2587,79 +2766,151 @@ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, perm
|
|
|
2587
2766
|
if (permission) {
|
|
2588
2767
|
body.permit = permission;
|
|
2589
2768
|
}
|
|
2590
|
-
let
|
|
2591
|
-
|
|
2592
|
-
response
|
|
2593
|
-
method: "POST",
|
|
2594
|
-
headers: {
|
|
2595
|
-
"Content-Type": "application/json"
|
|
2596
|
-
},
|
|
2597
|
-
body: JSON.stringify(body)
|
|
2598
|
-
});
|
|
2599
|
-
} catch (e) {
|
|
2600
|
-
throw new CofheError({
|
|
2601
|
-
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2602
|
-
message: `decrypt request failed`,
|
|
2603
|
-
hint: "Ensure the threshold network URL is valid and reachable.",
|
|
2604
|
-
cause: e instanceof Error ? e : void 0,
|
|
2605
|
-
context: {
|
|
2606
|
-
thresholdNetworkUrl,
|
|
2607
|
-
body
|
|
2608
|
-
}
|
|
2609
|
-
});
|
|
2610
|
-
}
|
|
2611
|
-
if (!response.ok) {
|
|
2612
|
-
let errorMessage = `HTTP ${response.status}`;
|
|
2769
|
+
let attemptIndex = 0;
|
|
2770
|
+
for (; ; ) {
|
|
2771
|
+
let response;
|
|
2613
2772
|
try {
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
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
|
+
});
|
|
2620
2792
|
}
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
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;
|
|
2630
2802
|
}
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
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
|
+
}
|
|
2637
2877
|
throw new CofheError({
|
|
2638
2878
|
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2639
|
-
message: `
|
|
2640
|
-
cause: e instanceof Error ? e : void 0,
|
|
2879
|
+
message: `decrypt submit response missing request_id`,
|
|
2641
2880
|
context: {
|
|
2642
2881
|
thresholdNetworkUrl,
|
|
2643
|
-
body
|
|
2882
|
+
body,
|
|
2883
|
+
submitResponse,
|
|
2884
|
+
attemptIndex
|
|
2644
2885
|
}
|
|
2645
2886
|
});
|
|
2646
2887
|
}
|
|
2647
|
-
const submitResponse = assertDecryptSubmitResponseV2(rawJson);
|
|
2648
|
-
return submitResponse.request_id;
|
|
2649
2888
|
}
|
|
2650
|
-
async function pollDecryptStatusV2(thresholdNetworkUrl, requestId) {
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
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) {
|
|
2655
2906
|
throw new CofheError({
|
|
2656
2907
|
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2657
|
-
message: `decrypt polling timed out after ${
|
|
2908
|
+
message: `decrypt polling timed out after ${DECRYPT_TIMEOUT_MS}ms`,
|
|
2658
2909
|
hint: "The request may still be processing. Try again later.",
|
|
2659
2910
|
context: {
|
|
2660
2911
|
thresholdNetworkUrl,
|
|
2661
2912
|
requestId,
|
|
2662
|
-
timeoutMs:
|
|
2913
|
+
timeoutMs: DECRYPT_TIMEOUT_MS
|
|
2663
2914
|
}
|
|
2664
2915
|
});
|
|
2665
2916
|
}
|
|
@@ -2731,45 +2982,14 @@ async function pollDecryptStatusV2(thresholdNetworkUrl, requestId) {
|
|
|
2731
2982
|
}
|
|
2732
2983
|
const statusResponse = assertDecryptStatusResponseV2(rawJson);
|
|
2733
2984
|
if (statusResponse.status === "COMPLETED") {
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
context: {
|
|
2740
|
-
thresholdNetworkUrl,
|
|
2741
|
-
requestId,
|
|
2742
|
-
statusResponse
|
|
2743
|
-
}
|
|
2744
|
-
});
|
|
2745
|
-
}
|
|
2746
|
-
if (statusResponse.error_message) {
|
|
2747
|
-
throw new CofheError({
|
|
2748
|
-
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
2749
|
-
message: `decrypt request failed: ${statusResponse.error_message}`,
|
|
2750
|
-
context: {
|
|
2751
|
-
thresholdNetworkUrl,
|
|
2752
|
-
requestId,
|
|
2753
|
-
statusResponse
|
|
2754
|
-
}
|
|
2755
|
-
});
|
|
2756
|
-
}
|
|
2757
|
-
if (!Array.isArray(statusResponse.decrypted)) {
|
|
2758
|
-
throw new CofheError({
|
|
2759
|
-
code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
|
|
2760
|
-
message: "decrypt completed but response missing <decrypted> byte array",
|
|
2761
|
-
context: {
|
|
2762
|
-
thresholdNetworkUrl,
|
|
2763
|
-
requestId,
|
|
2764
|
-
statusResponse
|
|
2765
|
-
}
|
|
2766
|
-
});
|
|
2767
|
-
}
|
|
2768
|
-
const decryptedValue = parseDecryptedBytesToBigInt(statusResponse.decrypted);
|
|
2769
|
-
const signature = normalizeTnSignature(statusResponse.signature);
|
|
2770
|
-
return { decryptedValue, signature };
|
|
2985
|
+
return parseCompletedDecryptResponseV2({
|
|
2986
|
+
value: statusResponse,
|
|
2987
|
+
thresholdNetworkUrl,
|
|
2988
|
+
requestId
|
|
2989
|
+
});
|
|
2771
2990
|
}
|
|
2772
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
2991
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
2992
|
+
attemptIndex += 1;
|
|
2773
2993
|
}
|
|
2774
2994
|
throw new CofheError({
|
|
2775
2995
|
code: "DECRYPT_FAILED" /* DecryptFailed */,
|
|
@@ -2780,9 +3000,21 @@ async function pollDecryptStatusV2(thresholdNetworkUrl, requestId) {
|
|
|
2780
3000
|
}
|
|
2781
3001
|
});
|
|
2782
3002
|
}
|
|
2783
|
-
async function tnDecryptV2(
|
|
2784
|
-
const
|
|
2785
|
-
|
|
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);
|
|
2786
3018
|
}
|
|
2787
3019
|
|
|
2788
3020
|
// core/decrypt/decryptForTxBuilder.ts
|
|
@@ -2791,6 +3023,7 @@ var DecryptForTxBuilder = class extends BaseBuilder {
|
|
|
2791
3023
|
permitHash;
|
|
2792
3024
|
permit;
|
|
2793
3025
|
permitSelection = "unset";
|
|
3026
|
+
pollCallback;
|
|
2794
3027
|
constructor(params) {
|
|
2795
3028
|
super({
|
|
2796
3029
|
config: params.config,
|
|
@@ -2816,6 +3049,10 @@ var DecryptForTxBuilder = class extends BaseBuilder {
|
|
|
2816
3049
|
getAccount() {
|
|
2817
3050
|
return this.account;
|
|
2818
3051
|
}
|
|
3052
|
+
onPoll(callback) {
|
|
3053
|
+
this.pollCallback = callback;
|
|
3054
|
+
return this;
|
|
3055
|
+
}
|
|
2819
3056
|
withPermit(permitOrPermitHash) {
|
|
2820
3057
|
if (this.permitSelection === "with-permit") {
|
|
2821
3058
|
throw new CofheError({
|
|
@@ -2943,7 +3180,13 @@ var DecryptForTxBuilder = class extends BaseBuilder {
|
|
|
2943
3180
|
this.assertPublicClient();
|
|
2944
3181
|
const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
|
|
2945
3182
|
const permission = permit ? PermitUtils.getPermission(permit, true) : null;
|
|
2946
|
-
const { decryptedValue, signature } = await tnDecryptV2(
|
|
3183
|
+
const { decryptedValue, signature } = await tnDecryptV2({
|
|
3184
|
+
ctHash: this.ctHash,
|
|
3185
|
+
chainId: this.chainId,
|
|
3186
|
+
permission,
|
|
3187
|
+
thresholdNetworkUrl,
|
|
3188
|
+
onPoll: this.pollCallback
|
|
3189
|
+
});
|
|
2947
3190
|
return {
|
|
2948
3191
|
ctHash: this.ctHash,
|
|
2949
3192
|
decryptedValue,
|
|
@@ -2961,7 +3204,6 @@ var DecryptForTxBuilder = class extends BaseBuilder {
|
|
|
2961
3204
|
const permit = await this.getResolvedPermit();
|
|
2962
3205
|
if (permit !== null) {
|
|
2963
3206
|
PermitUtils.validate(permit);
|
|
2964
|
-
PermitUtils.isValid(permit);
|
|
2965
3207
|
const chainId = permit._signedDomain.chainId;
|
|
2966
3208
|
if (chainId === hardhat$1.id) {
|
|
2967
3209
|
return await this.mocksDecryptForTx(permit);
|
|
@@ -2982,6 +3224,35 @@ var DecryptForTxBuilder = class extends BaseBuilder {
|
|
|
2982
3224
|
}
|
|
2983
3225
|
}
|
|
2984
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
|
+
}
|
|
2985
3256
|
|
|
2986
3257
|
// core/client.ts
|
|
2987
3258
|
var InitialConnectStore = {
|
|
@@ -3100,6 +3371,11 @@ function createCofheClientBase(opts) {
|
|
|
3100
3371
|
requireConnected: _requireConnected
|
|
3101
3372
|
});
|
|
3102
3373
|
}
|
|
3374
|
+
function verifyDecryptResult2(handle, cleartext, signature) {
|
|
3375
|
+
_requireConnected();
|
|
3376
|
+
const { publicClient } = connectStore.getState();
|
|
3377
|
+
return verifyDecryptResult(handle, cleartext, signature, publicClient);
|
|
3378
|
+
}
|
|
3103
3379
|
const _getChainIdAndAccount = (chainId, account) => {
|
|
3104
3380
|
const state = connectStore.getState();
|
|
3105
3381
|
const _chainId = chainId ?? state.chainId;
|
|
@@ -3182,6 +3458,7 @@ function createCofheClientBase(opts) {
|
|
|
3182
3458
|
},
|
|
3183
3459
|
// Utils (no context needed)
|
|
3184
3460
|
getHash: permits.getHash,
|
|
3461
|
+
export: permits.export,
|
|
3185
3462
|
serialize: permits.serialize,
|
|
3186
3463
|
deserialize: permits.deserialize
|
|
3187
3464
|
};
|
|
@@ -3210,6 +3487,7 @@ function createCofheClientBase(opts) {
|
|
|
3210
3487
|
*/
|
|
3211
3488
|
decryptHandle: decryptForView,
|
|
3212
3489
|
decryptForTx,
|
|
3490
|
+
verifyDecryptResult: verifyDecryptResult2,
|
|
3213
3491
|
permits: clientPermits
|
|
3214
3492
|
// Add SDK-specific methods below that require connection
|
|
3215
3493
|
// Example:
|
|
@@ -3220,4 +3498,4 @@ function createCofheClientBase(opts) {
|
|
|
3220
3498
|
};
|
|
3221
3499
|
}
|
|
3222
3500
|
|
|
3223
|
-
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 };
|