@zhoujun_aptos/octopus-ts-sdk-min 0.22.6 → 0.22.8

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.
@@ -34,6 +34,7 @@ __export(index_exports, {
34
34
  Enc: () => enc_exports,
35
35
  Group: () => group_exports,
36
36
  IBE: () => ibe_exports,
37
+ Result: () => Result,
37
38
  Sig: () => sig_exports,
38
39
  SilentSetupEncryption: () => silent_setup_encryption_exports,
39
40
  SilentSetupEncryptionXChain: () => silent_setup_encryption_xchain_exports,
@@ -752,7 +753,9 @@ __export(ibe_exports, {
752
753
  encrypt: () => encrypt4,
753
754
  encryptWithRandomness: () => encryptWithRandomness4,
754
755
  extract: () => extract2,
755
- keygen: () => keygen4
756
+ keygen: () => keygen4,
757
+ tryDecrypt: () => tryDecrypt2,
758
+ tryExtract: () => tryExtract
756
759
  });
757
760
  var import_ts_sdk6 = require("@aptos-labs/ts-sdk");
758
761
  var import_utils9 = require("@noble/curves/abstract/utils");
@@ -761,6 +764,84 @@ var import_utils9 = require("@noble/curves/abstract/utils");
761
764
  var import_bls12_381 = require("@noble/curves/bls12-381");
762
765
  var import_utils6 = require("@noble/curves/utils");
763
766
  var import_utils7 = require("@noble/hashes/utils");
767
+
768
+ // src/result.ts
769
+ var Result = class _Result {
770
+ isOk;
771
+ okValue;
772
+ errValue;
773
+ extra;
774
+ constructor({ isOk, okValue, errValue, extra }) {
775
+ this.isOk = isOk;
776
+ this.okValue = okValue;
777
+ this.errValue = errValue;
778
+ this.extra = extra;
779
+ }
780
+ static Ok(args) {
781
+ return new _Result({ isOk: true, okValue: args.value, extra: args.extra });
782
+ }
783
+ static Err(args) {
784
+ return new _Result({ isOk: false, errValue: args.error, extra: args.extra });
785
+ }
786
+ /**
787
+ * You write a closure that either returns a T or throws, and we wrap it to return a Result<T>.
788
+ * Your closure is also given an `extra` dictionary to record additional context.
789
+ */
790
+ static capture({ task, recordsExecutionTimeMs = false }) {
791
+ const start = performance.now();
792
+ var extra = {};
793
+ var error;
794
+ var okValue;
795
+ try {
796
+ okValue = task(extra);
797
+ } catch (caught) {
798
+ error = caught;
799
+ } finally {
800
+ if (recordsExecutionTimeMs) {
801
+ extra["_sdk_execution_time_ms"] = performance.now() - start;
802
+ }
803
+ if (error !== void 0) {
804
+ return _Result.Err({ error, extra });
805
+ } else {
806
+ return _Result.Ok({ value: okValue, extra });
807
+ }
808
+ }
809
+ }
810
+ /**
811
+ * You write an async closure that either returns a T or throws, and we wrap it to return a Result<T>.
812
+ * Your closure is also given an `extra` dictionary to record additional context.
813
+ */
814
+ static async captureAsync({ task, recordsExecutionTimeMs = false }) {
815
+ var extra = {};
816
+ const start = performance.now();
817
+ var error;
818
+ var okValue;
819
+ try {
820
+ okValue = await task(extra);
821
+ } catch (caught) {
822
+ error = caught;
823
+ } finally {
824
+ if (recordsExecutionTimeMs) {
825
+ extra["_sdk_execution_time_ms"] = performance.now() - start;
826
+ }
827
+ if (error !== void 0) {
828
+ return _Result.Err({ error, extra });
829
+ } else {
830
+ return _Result.Ok({ value: okValue, extra });
831
+ }
832
+ }
833
+ }
834
+ unwrapOrThrow(tothrow) {
835
+ if (!this.isOk) throw tothrow;
836
+ return this.okValue;
837
+ }
838
+ unwrapErrOrThrow(tothrow) {
839
+ if (this.isOk) throw tothrow;
840
+ return this.errValue;
841
+ }
842
+ };
843
+
844
+ // src/ibe/otp_hmac_boneh_franklin_bls12381_short_pk.ts
764
845
  var DST_OTP = new TextEncoder().encode("BONEH_FRANKLIN_BLS12381_SHORT_PK/OTP");
765
846
  var DST_ID_HASH = new TextEncoder().encode("BONEH_FRANKLIN_BLS12381_SHORT_PK/HASH_ID_TO_CURVE");
766
847
  var DST_MAC = new TextEncoder().encode("BONEH_FRANKLIN_BLS12381_SHORT_PK/MAC");
@@ -894,6 +975,21 @@ function decrypt3(identityKey, ciphertext) {
894
975
  const plaintext = xorBytes(otp, ciphertext.symmetricCiph);
895
976
  return plaintext;
896
977
  }
978
+ function tryDecrypt(identityKey, ciphertext) {
979
+ const task = (_extra) => {
980
+ const seedElementGt = import_bls12_381.bls12_381.pairing(ciphertext.c0, identityKey.privatePointG2);
981
+ const seed = bls12381GtReprNobleToAptos(import_bls12_381.bls12_381.fields.Fp12.toBytes(seedElementGt));
982
+ const macKey = kdf(seed, DST_MAC, 32);
983
+ const macAnother = hmac_sha3_256(macKey, ciphertext.symmetricCiph);
984
+ if ((0, import_utils7.bytesToHex)(ciphertext.mac) !== (0, import_utils7.bytesToHex)(macAnother)) {
985
+ throw "decryption failed";
986
+ }
987
+ const otp = kdf(seed, DST_OTP, ciphertext.symmetricCiph.length);
988
+ const plaintext = xorBytes(otp, ciphertext.symmetricCiph);
989
+ return plaintext;
990
+ };
991
+ return Result.capture({ task, recordsExecutionTimeMs: true });
992
+ }
897
993
 
898
994
  // src/ibe/index.ts
899
995
  var SCHEME_OTP_HAMC_BONEH_FRANKLIN_BLS12381_SHORT_PK = 0;
@@ -1124,12 +1220,36 @@ function extract2(privateKey, id) {
1124
1220
  }
1125
1221
  throw new Error(`Unknown scheme: ${privateKey.scheme}`);
1126
1222
  }
1223
+ function tryExtract(privateKey, id) {
1224
+ const task = (extra) => {
1225
+ extra["scheme"] = privateKey.scheme;
1226
+ if (privateKey.scheme == SCHEME_OTP_HAMC_BONEH_FRANKLIN_BLS12381_SHORT_PK) {
1227
+ return new IdentityPrivateKey2(
1228
+ SCHEME_OTP_HAMC_BONEH_FRANKLIN_BLS12381_SHORT_PK,
1229
+ extract(privateKey.inner, id)
1230
+ );
1231
+ }
1232
+ throw `unknown scheme`;
1233
+ };
1234
+ return Result.capture({ task, recordsExecutionTimeMs: true });
1235
+ }
1127
1236
  function decrypt4(identityKey, ciphertext) {
1128
1237
  if (identityKey.scheme == SCHEME_OTP_HAMC_BONEH_FRANKLIN_BLS12381_SHORT_PK) {
1129
1238
  return decrypt3(identityKey.inner, ciphertext.inner);
1130
1239
  }
1131
1240
  throw new Error(`Unknown scheme: ${identityKey.scheme}`);
1132
1241
  }
1242
+ function tryDecrypt2(identityKey, ciphertext) {
1243
+ const task = (extra) => {
1244
+ extra["scheme"] = identityKey.scheme;
1245
+ if (identityKey.scheme == SCHEME_OTP_HAMC_BONEH_FRANKLIN_BLS12381_SHORT_PK) {
1246
+ const innerResult = tryDecrypt(identityKey.inner, ciphertext.inner);
1247
+ return innerResult.unwrapOrThrow("OtpHmacBonehFranklinBls12381ShortPK.tryDecrypt failed");
1248
+ }
1249
+ throw `unknown scheme`;
1250
+ };
1251
+ return Result.capture({ task, recordsExecutionTimeMs: true });
1252
+ }
1133
1253
 
1134
1254
  // src/sig/index.ts
1135
1255
  var sig_exports = {};
@@ -2196,7 +2316,8 @@ __export(sym_exports, {
2196
2316
  decrypt: () => decrypt6,
2197
2317
  encrypt: () => encrypt6,
2198
2318
  encryptWithRandomness: () => encryptWithRandomness6,
2199
- keygen: () => keygen8
2319
+ keygen: () => keygen8,
2320
+ tryDecrypt: () => tryDecrypt4
2200
2321
  });
2201
2322
  var import_ts_sdk10 = require("@aptos-labs/ts-sdk");
2202
2323
  var import_utils15 = require("@noble/curves/utils");
@@ -2297,6 +2418,16 @@ function decrypt5(key, ciphertext) {
2297
2418
  return void 0;
2298
2419
  }
2299
2420
  }
2421
+ function tryDecrypt3(key, ciphertext) {
2422
+ const task = (_extra) => {
2423
+ const gcmInstance = (0, import_aes.gcm)(key.inner, ciphertext.iv);
2424
+ const encryptedData = new Uint8Array(ciphertext.ct.length + ciphertext.tag.length);
2425
+ encryptedData.set(ciphertext.ct, 0);
2426
+ encryptedData.set(ciphertext.tag, ciphertext.ct.length);
2427
+ return gcmInstance.decrypt(encryptedData);
2428
+ };
2429
+ return Result.capture({ task, recordsExecutionTimeMs: true });
2430
+ }
2300
2431
 
2301
2432
  // src/sym/index.ts
2302
2433
  var SCHEME_AES256GCM = 0;
@@ -2406,12 +2537,24 @@ function decrypt6(key, ciphertext) {
2406
2537
  }
2407
2538
  throw new Error("Invalid scheme");
2408
2539
  }
2540
+ function tryDecrypt4(key, ciphertext) {
2541
+ const task = (extra) => {
2542
+ extra["scheme"] = key.scheme;
2543
+ if (key.scheme === SCHEME_AES256GCM) {
2544
+ const innerResult = tryDecrypt3(key.inner, ciphertext.inner);
2545
+ return innerResult.unwrapOrThrow("AES256GCM decryption failed");
2546
+ }
2547
+ throw `unknown scheme`;
2548
+ };
2549
+ return Result.capture({ task, recordsExecutionTimeMs: true });
2550
+ }
2409
2551
 
2410
2552
  // src/worker_config.ts
2411
2553
  var worker_config_exports = {};
2412
2554
  __export(worker_config_exports, {
2413
2555
  WorkerConfig: () => WorkerConfig,
2414
2556
  get: () => get,
2557
+ getAsync: () => getAsync,
2415
2558
  randWorker: () => randWorker,
2416
2559
  view: () => view
2417
2560
  });
@@ -2501,6 +2644,23 @@ async function get(workerEndpoint) {
2501
2644
  const hex = await response.text();
2502
2645
  return WorkerConfig.fromHex(hex);
2503
2646
  }
2647
+ async function getAsync(workerEndpoint) {
2648
+ const task = async (extra) => {
2649
+ const url = `${workerEndpoint}/config_bcs`;
2650
+ extra["url"] = url;
2651
+ const response = await fetch(url, {
2652
+ method: "GET"
2653
+ });
2654
+ extra["responseStatus"] = response.status;
2655
+ extra["responseStatusText"] = response.statusText;
2656
+ if (!response.ok) {
2657
+ throw `Failed to fetch worker config: ${response.status} ${response.statusText}`;
2658
+ }
2659
+ const hex = await response.text();
2660
+ return WorkerConfig.fromHex(hex);
2661
+ };
2662
+ return await Result.captureAsync({ task, recordsExecutionTimeMs: true });
2663
+ }
2504
2664
  function randWorker() {
2505
2665
  const addr = new import_ts_sdk11.AccountAddress(utils_exports.randBytes(32));
2506
2666
  const encDk = keygen2();
@@ -2527,9 +2687,9 @@ __export(threshold_ibe_exports, {
2527
2687
  EncryptionKey: () => EncryptionKey2,
2528
2688
  FullDecryptionDomain: () => FullDecryptionDomain,
2529
2689
  ProofOfPermission: () => ProofOfPermission3,
2530
- decrypt: () => decrypt7,
2531
2690
  encrypt: () => encrypt7,
2532
- verifyAndExtract: () => verifyAndExtract
2691
+ tryDecrypt: () => tryDecrypt5,
2692
+ tryExtract: () => tryExtract2
2533
2693
  });
2534
2694
  var import_ts_sdk14 = require("@aptos-labs/ts-sdk");
2535
2695
  var import_utils19 = require("@noble/hashes/utils");
@@ -2717,64 +2877,76 @@ var ProofOfPermission = class _ProofOfPermission {
2717
2877
  }
2718
2878
  };
2719
2879
  async function verifyPermission({ fullDecryptionDomain, proof }) {
2720
- const aptos = createAptos(getChainNameFromChainId(fullDecryptionDomain.getAptosContractID().chainId));
2721
- const taskVerifySig = async () => {
2880
+ const task = async (extra) => {
2881
+ const aptos = createAptos(getChainNameFromChainId(fullDecryptionDomain.getAptosContractID().chainId));
2882
+ const [verifySigResult, checkAuthKeyResult, checkPermissionResult] = await Promise.all([
2883
+ verifySig({ aptos, fullDecryptionDomain, proof }),
2884
+ checkAuthKey({ aptos, userAddr: proof.userAddr, publicKey: proof.publicKey }),
2885
+ checkPermission({ aptos, fullDecryptionDomain, proof })
2886
+ ]);
2887
+ extra["verifySigResult"] = verifySigResult;
2888
+ extra["checkAuthKeyResult"] = checkAuthKeyResult;
2889
+ extra["checkPermissionResult"] = checkPermissionResult;
2890
+ if (!verifySigResult.isOk || !checkAuthKeyResult.isOk || !checkPermissionResult.isOk) {
2891
+ throw "one or more sub-checks failed";
2892
+ }
2893
+ };
2894
+ return await Result.captureAsync({ task, recordsExecutionTimeMs: true });
2895
+ }
2896
+ async function verifySig({ aptos, fullDecryptionDomain, proof }) {
2897
+ const task = async (extra) => {
2722
2898
  const msgToSign = fullDecryptionDomain.toPrettyMessage();
2723
2899
  const msgToSignHex = (0, import_utils17.bytesToHex)(new TextEncoder().encode(msgToSign));
2724
- const fullMessageFromPetra = proof.fullMessage.includes(msgToSign);
2725
- const fullMessageFromAptosConnect = proof.fullMessage.includes(msgToSignHex);
2726
- if (!fullMessageFromPetra && !fullMessageFromAptosConnect) return false;
2727
- try {
2728
- return await proof.publicKey.verifySignatureAsync({
2729
- aptosConfig: aptos.config,
2730
- message: proof.fullMessage,
2731
- signature: proof.signature
2732
- });
2733
- } catch (error) {
2734
- return false;
2735
- }
2900
+ const fullMessageSeemsFromPetra = proof.fullMessage.includes(msgToSign);
2901
+ const fullMessageSeemsFromAptosConnect = proof.fullMessage.includes(msgToSignHex);
2902
+ extra["msgToSign"] = msgToSign;
2903
+ extra["msgToSignHex"] = msgToSignHex;
2904
+ extra["fullMessageSeemsFromPetra"] = fullMessageSeemsFromPetra;
2905
+ extra["fullMessageSeemsFromAptosConnect"] = fullMessageSeemsFromAptosConnect;
2906
+ if (!fullMessageSeemsFromPetra && !fullMessageSeemsFromAptosConnect) throw "fullMessage does not contain fullDecryptionDomain or its hex";
2907
+ const sigValid = await proof.publicKey.verifySignatureAsync({
2908
+ aptosConfig: aptos.config,
2909
+ message: proof.fullMessage,
2910
+ signature: proof.signature
2911
+ });
2912
+ if (!sigValid) throw "verifySignatureAsync failed";
2736
2913
  };
2737
- const taskCheckAuthKey = async () => {
2738
- try {
2739
- const onChainAuthKeyBytes = await getAccountAuthKeyBytes(aptos, proof.userAddr);
2740
- const userAuthKeyBytes = proof.publicKey.authKey().bcsToBytes();
2741
- const onChainHex = (0, import_utils17.bytesToHex)(onChainAuthKeyBytes);
2742
- const userHex = (0, import_utils17.bytesToHex)(userAuthKeyBytes);
2743
- console.log(`onChainHex: ${onChainHex}`);
2744
- console.log(`userHex : ${userHex}`);
2745
- return onChainHex === userHex;
2746
- } catch (error) {
2747
- return false;
2914
+ return await Result.captureAsync({ task, recordsExecutionTimeMs: true });
2915
+ }
2916
+ async function checkAuthKey({ aptos, userAddr, publicKey }) {
2917
+ const task = async (extra) => {
2918
+ if (!(publicKey instanceof import_ts_sdk12.AccountPublicKey)) {
2919
+ throw "publicKey is not an AccountPublicKey";
2748
2920
  }
2921
+ const onChainAuthKeyBytes = await getAccountAuthKeyBytes(aptos, userAddr);
2922
+ const userAuthKeyBytes = publicKey.authKey().bcsToBytes();
2923
+ const onChainHex = (0, import_utils17.bytesToHex)(onChainAuthKeyBytes);
2924
+ const userHex = (0, import_utils17.bytesToHex)(userAuthKeyBytes);
2925
+ extra["onChainHex"] = onChainHex;
2926
+ extra["userHex"] = userHex;
2927
+ if (onChainHex !== userHex) throw "on-chain auth key does not match user auth key";
2749
2928
  };
2750
- const taskCheckPermission = async () => {
2929
+ return await Result.captureAsync({ task, recordsExecutionTimeMs: true });
2930
+ }
2931
+ async function checkPermission({ aptos, fullDecryptionDomain, proof }) {
2932
+ const task = async (extra) => {
2751
2933
  const contractId = fullDecryptionDomain.getAptosContractID();
2752
- try {
2753
- const userIsPermittedMoveVal = await view2(
2754
- aptos,
2755
- `${contractId.moduleAddr.toStringLong()}::${contractId.moduleName}::${contractId.functionName}`,
2756
- [],
2757
- [proof.userAddr, fullDecryptionDomain.domain]
2758
- );
2759
- return userIsPermittedMoveVal?.toString() === "true";
2760
- } catch (error) {
2761
- return false;
2934
+ const viewFunctionInvocationResult = await view2({
2935
+ aptos,
2936
+ func: `${contractId.moduleAddr.toStringLong()}::${contractId.moduleName}::${contractId.functionName}`,
2937
+ typeArguments: [],
2938
+ functionArguments: [proof.userAddr, fullDecryptionDomain.domain]
2939
+ });
2940
+ extra["viewFunctionInvocationResult"] = viewFunctionInvocationResult;
2941
+ if (!viewFunctionInvocationResult.isOk) {
2942
+ throw "view function invocation failed";
2943
+ }
2944
+ const returnedMoveValue = viewFunctionInvocationResult.okValue;
2945
+ if (returnedMoveValue?.toString() !== "true") {
2946
+ throw "access control contract return value is not true";
2762
2947
  }
2763
2948
  };
2764
- const [sigIsValid, authKeyMatches, userIsPermitted] = await Promise.all([
2765
- taskVerifySig(),
2766
- taskCheckAuthKey(),
2767
- taskCheckPermission()
2768
- ]);
2769
- if (!sigIsValid) {
2770
- throw new Error("Signature invalid.");
2771
- }
2772
- if (!authKeyMatches) {
2773
- throw new Error("Authentication key mismatch: on-chain key does not match provided public key.");
2774
- }
2775
- if (!userIsPermitted) {
2776
- throw new Error("Permission denied.");
2777
- }
2949
+ return await Result.captureAsync({ task, recordsExecutionTimeMs: true });
2778
2950
  }
2779
2951
  function getChainNameFromChainId(chainId) {
2780
2952
  if (chainId === 1) {
@@ -2811,18 +2983,22 @@ async function getAccountAuthKeyBytes(aptos, address) {
2811
2983
  const accountInfo = await aptos.getAccountInfo({ accountAddress: address });
2812
2984
  return (0, import_utils17.hexToBytes)(accountInfo.authentication_key.replace("0x", ""));
2813
2985
  }
2814
- async function view2(aptos, func, typeArguments, functionArguments) {
2815
- const result = await aptos.view({
2816
- payload: {
2817
- function: func,
2818
- typeArguments,
2819
- functionArguments
2986
+ async function view2({ aptos, func, typeArguments, functionArguments }) {
2987
+ const task = async (extra) => {
2988
+ const returnedMoveValues = await aptos.view({
2989
+ payload: {
2990
+ function: func,
2991
+ typeArguments,
2992
+ functionArguments
2993
+ }
2994
+ });
2995
+ extra["returnedMoveValues"] = returnedMoveValues;
2996
+ if (returnedMoveValues.length === 0) {
2997
+ throw `aptos.view returned an empty list`;
2820
2998
  }
2821
- });
2822
- if (result.length === 0) {
2823
- throw new Error(`View function returned empty result`);
2824
- }
2825
- return result[0];
2999
+ return returnedMoveValues[0];
3000
+ };
3001
+ return await Result.captureAsync({ task, recordsExecutionTimeMs: true });
2826
3002
  }
2827
3003
 
2828
3004
  // src/threshold-ibe/solana.ts
@@ -2923,76 +3099,107 @@ var ProofOfPermission2 = class _ProofOfPermission {
2923
3099
  }
2924
3100
  };
2925
3101
  async function verifyPermission2({ fullDecryptionDomain, proof }) {
2926
- const txn = proof.inner;
2927
- assertTransactionValid({ txn, fullDecryptionDomain });
2928
- await assertTransactionSimulationPasses(txn, fullDecryptionDomain.getSolanaContractID().knownChainName);
2929
- }
2930
- function assertTransactionValid({ txn, fullDecryptionDomain }) {
2931
- let instructions;
2932
- if (txn instanceof import_web3.VersionedTransaction) {
2933
- const message = txn.message;
2934
- instructions = message.compiledInstructions.map((ix) => {
2935
- if (ix.programIdIndex >= message.staticAccountKeys.length) {
2936
- throw new Error(`Program ID index ${ix.programIdIndex} is out of bounds for static account keys (length: ${message.staticAccountKeys.length}). Address table lookups are not supported for validation.`);
2937
- }
2938
- const programId = message.staticAccountKeys[ix.programIdIndex];
2939
- return { programId, data: Buffer.from(ix.data) };
2940
- });
2941
- } else {
2942
- instructions = txn.instructions.map((ix) => ({
2943
- programId: ix.programId,
2944
- data: Buffer.from(ix.data)
2945
- }));
2946
- }
2947
- if (instructions.length !== 1) {
2948
- throw new Error(`transaction must contain exactly 1 instruction, found ${instructions.length}`);
2949
- }
2950
- const instruction = instructions[0];
2951
- if (!instruction.programId.equals(fullDecryptionDomain.getSolanaContractID().programId)) {
2952
- throw new Error(`transaction instruction program ID (${instruction.programId.toString()}) does not match contract program ID`);
2953
- }
2954
- const instructionData = instruction.data;
2955
- if (instructionData.length < 12) {
2956
- throw new Error("instruction data too short (must be at least 12 bytes: 8-byte discriminator + 4-byte Vec length)");
2957
- }
2958
- const paramData = instructionData.slice(8);
2959
- const vecLength = paramData.readUInt32LE(0);
2960
- if (paramData.length < 4 + vecLength) {
2961
- throw new Error(`instruction data incomplete: expected ${4 + vecLength} bytes after discriminator, found ${paramData.length}`);
2962
- }
2963
- const fullBlobNameBytes = paramData.slice(4, 4 + vecLength);
2964
- const expectedParamDataLength = 4 + vecLength;
2965
- if (paramData.length > expectedParamDataLength) {
2966
- throw new Error(`instruction data has extra bytes: expected exactly ${expectedParamDataLength} bytes after discriminator, found ${paramData.length}`);
3102
+ var extra = {};
3103
+ try {
3104
+ const txn = proof.inner;
3105
+ const validateTxnResult = validateTxn({ txn, fullDecryptionDomain });
3106
+ if (!validateTxnResult.isOk) {
3107
+ extra["causedBy"] = validateTxnResult.extra;
3108
+ throw "transaction is invalid";
3109
+ }
3110
+ const simulationResult = await assertTransactionSimulationPasses(txn, fullDecryptionDomain.getSolanaContractID().knownChainName);
3111
+ if (!simulationResult.isOk) {
3112
+ extra["causedBy"] = simulationResult.extra;
3113
+ throw "transaction simulation failed";
3114
+ }
3115
+ return Result.Ok({ value: void 0, extra });
3116
+ } catch (error) {
3117
+ return Result.Err({ error, extra });
2967
3118
  }
2968
- if ((0, import_utils18.bytesToHex)(fullBlobNameBytes) !== fullDecryptionDomain.toHex()) {
2969
- throw new Error(`domain mismatch: instruction parameter does not match decryptionContext.domain`);
3119
+ }
3120
+ function validateTxn({ txn, fullDecryptionDomain }) {
3121
+ try {
3122
+ let instructions;
3123
+ if (txn instanceof import_web3.VersionedTransaction) {
3124
+ const message = txn.message;
3125
+ instructions = message.compiledInstructions.map((ix) => {
3126
+ if (ix.programIdIndex >= message.staticAccountKeys.length) {
3127
+ throw `some program ID index is out of bounds for static account keys (are you using address table lookups? threshold-ibe does not support it yet)`;
3128
+ }
3129
+ const programId = message.staticAccountKeys[ix.programIdIndex];
3130
+ return { programId, data: Buffer.from(ix.data) };
3131
+ });
3132
+ } else {
3133
+ instructions = txn.instructions.map((ix) => ({
3134
+ programId: ix.programId,
3135
+ data: Buffer.from(ix.data)
3136
+ }));
3137
+ }
3138
+ if (instructions.length !== 1) throw `transaction must contain exactly 1 instruction`;
3139
+ const instruction = instructions[0];
3140
+ if (!instruction.programId.equals(fullDecryptionDomain.getSolanaContractID().programId)) {
3141
+ throw `transaction instruction program ID does not match contract program ID`;
3142
+ }
3143
+ const instructionData = instruction.data;
3144
+ if (instructionData.length < 12) {
3145
+ throw "instruction data too short";
3146
+ }
3147
+ const paramData = instructionData.slice(8);
3148
+ const vecLength = paramData.readUInt32LE(0);
3149
+ if (paramData.length < 4 + vecLength) throw `instruction data incomplete`;
3150
+ const domainAsTxnParam = paramData.slice(4, 4 + vecLength);
3151
+ const expectedParamDataLength = 4 + vecLength;
3152
+ if (paramData.length > expectedParamDataLength) {
3153
+ throw `instruction data has extra bytes`;
3154
+ }
3155
+ if ((0, import_utils18.bytesToHex)(domainAsTxnParam) !== (0, import_utils18.bytesToHex)(fullDecryptionDomain.domain)) {
3156
+ throw `instruction parameter does not match decryptionContext.domain`;
3157
+ }
3158
+ return Result.Ok({ value: void 0 });
3159
+ } catch (error) {
3160
+ return Result.Err({ error });
2970
3161
  }
2971
3162
  }
2972
3163
  async function assertTransactionSimulationPasses(txn, chainName) {
2973
- let rpcUrl;
2974
- if (chainName === "localnet" || chainName === "localhost") {
2975
- rpcUrl = "http://127.0.0.1:8899";
2976
- } else if (chainName === "devnet") {
2977
- rpcUrl = "https://api.devnet.solana.com";
2978
- } else if (chainName === "testnet") {
2979
- rpcUrl = "https://api.testnet.solana.com";
2980
- } else if (chainName === "mainnet-beta") {
2981
- rpcUrl = "https://api.mainnet-beta.solana.com";
2982
- } else {
2983
- throw new Error(`Unknown chain name: ${chainName}`);
2984
- }
2985
- const connection = new import_web3.Connection(rpcUrl, "confirmed");
2986
- let simulation;
2987
- if (txn instanceof import_web3.VersionedTransaction) {
2988
- simulation = await connection.simulateTransaction(txn, {
2989
- sigVerify: true
2990
- });
2991
- } else {
2992
- simulation = await connection.simulateTransaction(txn);
2993
- }
2994
- if (simulation.value.err) {
2995
- throw new Error(`transaction simulation failed: ${JSON.stringify(simulation.value.err)}`);
3164
+ var extra = {};
3165
+ var error;
3166
+ const start = performance.now();
3167
+ try {
3168
+ let rpcUrl;
3169
+ if (chainName === "localnet" || chainName === "localhost") {
3170
+ rpcUrl = "http://127.0.0.1:8899";
3171
+ } else if (chainName === "devnet") {
3172
+ rpcUrl = "https://api.devnet.solana.com";
3173
+ } else if (chainName === "testnet") {
3174
+ rpcUrl = "https://api.testnet.solana.com";
3175
+ } else if (chainName === "mainnet-beta") {
3176
+ rpcUrl = "https://api.mainnet-beta.solana.com";
3177
+ } else {
3178
+ extra["chainName"] = chainName;
3179
+ throw `unsupported chain name`;
3180
+ }
3181
+ const connection = new import_web3.Connection(rpcUrl, "confirmed");
3182
+ let simulation;
3183
+ if (txn instanceof import_web3.VersionedTransaction) {
3184
+ simulation = await connection.simulateTransaction(txn, {
3185
+ sigVerify: true
3186
+ });
3187
+ } else {
3188
+ simulation = await connection.simulateTransaction(txn);
3189
+ }
3190
+ if (simulation.value.err) {
3191
+ extra["simulationError"] = simulation.value.err;
3192
+ throw `transaction simulation failed`;
3193
+ }
3194
+ } catch (caught) {
3195
+ error = caught;
3196
+ } finally {
3197
+ extra["executionTimeMs"] = performance.now() - start;
3198
+ if (error !== void 0) {
3199
+ return Result.Err({ error, extra });
3200
+ } else {
3201
+ return Result.Ok({ value: void 0, extra });
3202
+ }
2996
3203
  }
2997
3204
  }
2998
3205
 
@@ -3098,11 +3305,16 @@ var EncryptionKey2 = class _EncryptionKey {
3098
3305
  this.ibeMpks = ibeMpks;
3099
3306
  }
3100
3307
  static async fetch({ committee }) {
3101
- const ibeMpks = await Promise.all(committee.workerEndpoints.map(async (endpoint) => {
3102
- const config = await worker_config_exports.get(endpoint);
3103
- return config.ibeMpk;
3104
- }));
3105
- return new _EncryptionKey({ ibeMpks });
3308
+ const task = async (extra) => {
3309
+ const workerConfigGetResults = await Promise.all(committee.workerEndpoints.map(async (endpoint) => {
3310
+ return worker_config_exports.getAsync(endpoint);
3311
+ }));
3312
+ extra["workerConfigGetResults"] = workerConfigGetResults;
3313
+ if (workerConfigGetResults.some((result) => !result.isOk)) throw "failed to get all worker configs";
3314
+ const ibeMpks = workerConfigGetResults.map((result) => result.okValue.ibeMpk);
3315
+ return new _EncryptionKey({ ibeMpks });
3316
+ };
3317
+ return await Result.captureAsync({ task, recordsExecutionTimeMs: true });
3106
3318
  }
3107
3319
  };
3108
3320
  var DecryptionKey2 = class _DecryptionKey {
@@ -3111,43 +3323,43 @@ var DecryptionKey2 = class _DecryptionKey {
3111
3323
  this.ibeDecryptionKeys = ibeDecryptionKeys;
3112
3324
  }
3113
3325
  static async fetch({ committee, contractId, domain, proof }) {
3114
- const decKeyLoadResults = await Promise.all(committee.workerEndpoints.map(async (workerEndpoint) => {
3115
- const task = WorkerTask.newThresholdIbeDecryptionKey({ committee, contractId, domain, proof });
3326
+ const task = async (extra) => {
3327
+ extra["committee"] = committee;
3328
+ const decKeyLoadResults = await Promise.all(committee.workerEndpoints.map(async (_workerEndpoint, index) => {
3329
+ return _DecryptionKey.fetchDecKeyShare({ committee, contractId, domain, proof, index });
3330
+ }));
3331
+ extra["decKeyLoadResults"] = decKeyLoadResults;
3332
+ const numSharesCollected = decKeyLoadResults.filter((loadResult) => loadResult.isOk).length;
3333
+ if (numSharesCollected < committee.threshold) throw `failed to collect enough shares`;
3334
+ const decKeyShares = decKeyLoadResults.map((loadResult) => loadResult.okValue ?? null);
3335
+ return new _DecryptionKey(decKeyShares);
3336
+ };
3337
+ return Result.captureAsync({ task, recordsExecutionTimeMs: true });
3338
+ }
3339
+ static async fetchDecKeyShare({ committee, contractId, domain, proof, index }) {
3340
+ const task = async (extra) => {
3341
+ const targetWorkerEndpoint = committee.workerEndpoints[index];
3342
+ const task2 = WorkerTask.newThresholdIbeDecryptionKey({ committee, contractId, domain, proof });
3116
3343
  const controller = new AbortController();
3117
3344
  const timeoutId = setTimeout(() => controller.abort(), 5e3);
3118
3345
  var response = null;
3119
3346
  try {
3120
- response = await fetch(workerEndpoint, {
3347
+ response = await fetch(targetWorkerEndpoint, {
3121
3348
  method: "POST",
3122
- body: task.toHex(),
3349
+ body: task2.toHex(),
3123
3350
  signal: controller.signal
3124
3351
  });
3125
3352
  } catch (error) {
3126
3353
  clearTimeout(timeoutId);
3127
3354
  }
3128
- if (response == null) {
3129
- return new WorkerTimedOut();
3130
- }
3355
+ if (response == null) throw "worker is not responding";
3131
3356
  const responseBody = await response.text();
3132
- if (response.status !== 200) {
3133
- return new WorkerRejected(response.status, responseBody);
3134
- }
3135
- try {
3136
- return IdentityPrivateKey2.fromHex(responseBody);
3137
- } catch (error) {
3138
- return new CouldNotParseDecryptionKey(responseBody);
3139
- }
3140
- }));
3141
- const numSharesCollected = decKeyLoadResults.filter((loadResult) => loadResult instanceof IdentityPrivateKey2).length;
3142
- if (numSharesCollected < committee.threshold) {
3143
- const workerResults = committee.workerEndpoints.map((workerEndpoint, i) => {
3144
- const result = decKeyLoadResults[i];
3145
- return `${workerEndpoint}: ${result instanceof IdentityPrivateKey2 ? "Success" : result.toDisplayString()}`;
3146
- }).join(", ");
3147
- throw new Error(`Failed to collect enough shares to decrypt. Collected ${numSharesCollected} shares, but needed ${committee.threshold} shares. Worker results: ${workerResults}`);
3148
- }
3149
- const decKeys = decKeyLoadResults.map((loadResult) => loadResult instanceof IdentityPrivateKey2 ? loadResult : null);
3150
- return new _DecryptionKey(decKeys);
3357
+ extra["workerResponseStatus"] = response.status;
3358
+ extra["workerResponseBody"] = responseBody;
3359
+ if (response.status !== 200) throw `worker rejected: ${response.status}`;
3360
+ return IdentityPrivateKey2.fromHex(responseBody);
3361
+ };
3362
+ return Result.captureAsync({ task, recordsExecutionTimeMs: true });
3151
3363
  }
3152
3364
  };
3153
3365
  var Ciphertext7 = class _Ciphertext {
@@ -3328,56 +3540,48 @@ function encrypt7({ committee, encryptionKey, contractId, domain, plaintext }) {
3328
3540
  const ibeCiphs = symmKeyShares.map((share, idx) => encrypt4(encryptionKey.ibeMpks[idx], fullDecryptionDomain.toBytes(), share.payload));
3329
3541
  return { fullDecryptionDomain, ciphertext: new Ciphertext7(symmCiph, ibeCiphs) };
3330
3542
  }
3331
- function decrypt7({ decryptionKey, ciphertext }) {
3332
- if (decryptionKey.ibeDecryptionKeys.length !== ciphertext.ibeCiphs.length) {
3333
- throw new Error("decryptionKey.ibeDecryptionKeys.length !== ciphertext.ibeCiphs.length");
3334
- }
3335
- const symmKeyShares = ciphertext.ibeCiphs.map((ibeCiph, idx) => {
3336
- if (decryptionKey.ibeDecryptionKeys[idx] == null) return null;
3337
- const sharePayload = decrypt4(decryptionKey.ibeDecryptionKeys[idx], ibeCiph);
3338
- if (sharePayload == null) return null;
3339
- return new Share(idx + 1, sharePayload);
3340
- }).filter((share) => share != null);
3341
- const symmKeyBytes = combine(symmKeyShares);
3342
- const symmKey = Key2.fromBytes(symmKeyBytes);
3343
- return decrypt6(symmKey, ciphertext.aesCiph);
3543
+ function tryDecrypt5({ decryptionKey, ciphertext }) {
3544
+ const task = (extra) => {
3545
+ extra["numIbeDecryptionKeys"] = decryptionKey.ibeDecryptionKeys.length;
3546
+ extra["numIbeCiphs"] = ciphertext.ibeCiphs.length;
3547
+ if (decryptionKey.ibeDecryptionKeys.length !== ciphertext.ibeCiphs.length) {
3548
+ throw "decryption key share count does not match ciphertext share count";
3549
+ }
3550
+ const unfilteredSymmKeyShares = ciphertext.ibeCiphs.map((ibeCiph, idx) => {
3551
+ if (decryptionKey.ibeDecryptionKeys[idx] == null) return null;
3552
+ const maybeSharePayload = tryDecrypt2(decryptionKey.ibeDecryptionKeys[idx], ibeCiph);
3553
+ if (!maybeSharePayload.isOk) return null;
3554
+ return new Share(idx + 1, maybeSharePayload.okValue);
3555
+ });
3556
+ extra["symmKeySharePresences"] = unfilteredSymmKeyShares.map((share) => share != null);
3557
+ const symmKeyShares = unfilteredSymmKeyShares.filter((share) => share != null);
3558
+ const symmKeyBytes = combine(symmKeyShares);
3559
+ const symmKey = Key2.fromBytes(symmKeyBytes);
3560
+ const symDecResult = tryDecrypt4(symmKey, ciphertext.aesCiph);
3561
+ return symDecResult.unwrapOrThrow("symmetric decryption failed");
3562
+ };
3563
+ return Result.capture({ task, recordsExecutionTimeMs: true });
3344
3564
  }
3345
- async function verifyAndExtract({ ibeMsk, committee, contractId, domain, proof }) {
3346
- const decryptionContext = new FullDecryptionDomain({ committee, contractId, domain });
3347
- if (contractId.scheme == ContractID3.SCHEME_APTOS && proof.scheme == ProofOfPermission3.SCHEME_APTOS) {
3348
- await verifyPermission({ fullDecryptionDomain: decryptionContext, proof: proof.inner });
3349
- } else if (contractId.scheme == ContractID3.SCHEME_SOLANA && proof.scheme == ProofOfPermission3.SCHEME_SOLANA) {
3350
- await verifyPermission2({ fullDecryptionDomain: decryptionContext, proof: proof.inner });
3351
- } else {
3352
- throw new Error(`Unknown scheme: contractId=${contractId.scheme}, proof=${proof.scheme}`);
3353
- }
3354
- return extract2(ibeMsk, decryptionContext.toBytes());
3565
+ async function tryExtract2({ ibeMsk, committee, contractId, domain, proof }) {
3566
+ const task = async (extra) => {
3567
+ extra["contractIdScheme"] = contractId.scheme;
3568
+ extra["proofScheme"] = proof.scheme;
3569
+ const decryptionContext = new FullDecryptionDomain({ committee, contractId, domain });
3570
+ if (contractId.scheme == ContractID3.SCHEME_APTOS && proof.scheme == ProofOfPermission3.SCHEME_APTOS) {
3571
+ const aptosResult = await verifyPermission({ fullDecryptionDomain: decryptionContext, proof: proof.inner });
3572
+ extra["verifyAptosResult"] = aptosResult;
3573
+ if (!aptosResult.isOk) throw "aptos verification failed";
3574
+ } else if (contractId.scheme == ContractID3.SCHEME_SOLANA && proof.scheme == ProofOfPermission3.SCHEME_SOLANA) {
3575
+ const solanaResult = await verifyPermission2({ fullDecryptionDomain: decryptionContext, proof: proof.inner });
3576
+ extra["verifySolanaResult"] = solanaResult;
3577
+ if (!solanaResult.isOk) throw "solana verification failed";
3578
+ } else {
3579
+ throw "unsupported scheme combination";
3580
+ }
3581
+ return extract2(ibeMsk, decryptionContext.toBytes());
3582
+ };
3583
+ return Result.captureAsync({ task, recordsExecutionTimeMs: true });
3355
3584
  }
3356
- var WorkerTimedOut = class {
3357
- toDisplayString() {
3358
- return "Timed out";
3359
- }
3360
- };
3361
- var WorkerRejected = class {
3362
- statusCode;
3363
- responseBody;
3364
- constructor(statusCode, responseBody) {
3365
- this.statusCode = statusCode;
3366
- this.responseBody = responseBody;
3367
- }
3368
- toDisplayString() {
3369
- return `Rejected: ${this.statusCode} ${this.responseBody}`;
3370
- }
3371
- };
3372
- var CouldNotParseDecryptionKey = class {
3373
- originalHex;
3374
- constructor(originalHex) {
3375
- this.originalHex = originalHex;
3376
- }
3377
- toDisplayString() {
3378
- return `Could not parse decryption key: ${this.originalHex}`;
3379
- }
3380
- };
3381
3585
 
3382
3586
  // src/worker_task.ts
3383
3587
  var TYPE_SILENT_SETUP_DECRYPTION_KEY = 5;
@@ -3607,16 +3811,16 @@ var DecryptionContext = class _DecryptionContext {
3607
3811
  clearTimeout(timeoutId);
3608
3812
  }
3609
3813
  if (response == null) {
3610
- return new WorkerTimedOut2();
3814
+ return new WorkerTimedOut();
3611
3815
  }
3612
3816
  const responseBody = await response.text();
3613
3817
  if (response.status !== 200) {
3614
- return new WorkerRejected2(response.status, responseBody);
3818
+ return new WorkerRejected(response.status, responseBody);
3615
3819
  }
3616
3820
  try {
3617
3821
  return IdentityPrivateKey2.fromHex(responseBody);
3618
3822
  } catch (error) {
3619
- return new CouldNotParseDecryptionKey2(responseBody);
3823
+ return new CouldNotParseDecryptionKey(responseBody);
3620
3824
  }
3621
3825
  }));
3622
3826
  const numSharesCollected = decKeyLoadResults.filter((loadResult) => loadResult instanceof IdentityPrivateKey2).length;
@@ -3722,12 +3926,12 @@ var Decryptor = class {
3722
3926
  return decrypt6(symmKey, ciphertext.aesCiph);
3723
3927
  }
3724
3928
  };
3725
- var WorkerTimedOut2 = class {
3929
+ var WorkerTimedOut = class {
3726
3930
  toDisplayString() {
3727
3931
  return "Timed out";
3728
3932
  }
3729
3933
  };
3730
- var WorkerRejected2 = class {
3934
+ var WorkerRejected = class {
3731
3935
  statusCode;
3732
3936
  responseBody;
3733
3937
  constructor(statusCode, responseBody) {
@@ -3738,7 +3942,7 @@ var WorkerRejected2 = class {
3738
3942
  return `Rejected: ${this.statusCode} ${this.responseBody}`;
3739
3943
  }
3740
3944
  };
3741
- var CouldNotParseDecryptionKey2 = class {
3945
+ var CouldNotParseDecryptionKey = class {
3742
3946
  originalHex;
3743
3947
  constructor(originalHex) {
3744
3948
  this.originalHex = originalHex;
@@ -4302,16 +4506,16 @@ var DecryptionContext2 = class _DecryptionContext {
4302
4506
  clearTimeout(timeoutId);
4303
4507
  }
4304
4508
  if (response == null) {
4305
- return new WorkerTimedOut3();
4509
+ return new WorkerTimedOut2();
4306
4510
  }
4307
4511
  const responseBody = await response.text();
4308
4512
  if (response.status !== 200) {
4309
- return new WorkerRejected3(response.status, responseBody);
4513
+ return new WorkerRejected2(response.status, responseBody);
4310
4514
  }
4311
4515
  try {
4312
4516
  return IdentityPrivateKey2.fromHex(responseBody);
4313
4517
  } catch (error) {
4314
- return new CouldNotParseDecryptionKey3(responseBody);
4518
+ return new CouldNotParseDecryptionKey2(responseBody);
4315
4519
  }
4316
4520
  }));
4317
4521
  const numSharesCollected = decKeyLoadResults.filter((loadResult) => loadResult instanceof IdentityPrivateKey2).length;
@@ -4416,12 +4620,12 @@ var Decryptor2 = class {
4416
4620
  return decrypt6(symmKey, ciphertext.aesCiph);
4417
4621
  }
4418
4622
  };
4419
- var WorkerTimedOut3 = class {
4623
+ var WorkerTimedOut2 = class {
4420
4624
  toDisplayString() {
4421
4625
  return "Timed out";
4422
4626
  }
4423
4627
  };
4424
- var WorkerRejected3 = class {
4628
+ var WorkerRejected2 = class {
4425
4629
  statusCode;
4426
4630
  responseBody;
4427
4631
  constructor(statusCode, responseBody) {
@@ -4432,7 +4636,7 @@ var WorkerRejected3 = class {
4432
4636
  return `Rejected: ${this.statusCode} ${this.responseBody}`;
4433
4637
  }
4434
4638
  };
4435
- var CouldNotParseDecryptionKey3 = class {
4639
+ var CouldNotParseDecryptionKey2 = class {
4436
4640
  originalHex;
4437
4641
  constructor(originalHex) {
4438
4642
  this.originalHex = originalHex;
@@ -4479,6 +4683,7 @@ var RequestForDecryptionKey2 = class _RequestForDecryptionKey {
4479
4683
  Enc,
4480
4684
  Group,
4481
4685
  IBE,
4686
+ Result,
4482
4687
  Sig,
4483
4688
  SilentSetupEncryption,
4484
4689
  SilentSetupEncryptionXChain,