@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.
@@ -711,7 +711,9 @@ __export(ibe_exports, {
711
711
  encrypt: () => encrypt4,
712
712
  encryptWithRandomness: () => encryptWithRandomness4,
713
713
  extract: () => extract2,
714
- keygen: () => keygen4
714
+ keygen: () => keygen4,
715
+ tryDecrypt: () => tryDecrypt2,
716
+ tryExtract: () => tryExtract
715
717
  });
716
718
  import { Deserializer as Deserializer5, Serializer as Serializer6 } from "@aptos-labs/ts-sdk";
717
719
  import { bytesToHex as bytesToHex5, hexToBytes as hexToBytes2 } from "@noble/curves/abstract/utils";
@@ -720,6 +722,84 @@ import { bytesToHex as bytesToHex5, hexToBytes as hexToBytes2 } from "@noble/cur
720
722
  import { bls12_381 } from "@noble/curves/bls12-381";
721
723
  import { bytesToNumberBE, bytesToNumberLE as bytesToNumberLE2, numberToBytesLE as numberToBytesLE2 } from "@noble/curves/utils";
722
724
  import { bytesToHex as bytesToHex4, randomBytes } from "@noble/hashes/utils";
725
+
726
+ // src/result.ts
727
+ var Result = class _Result {
728
+ isOk;
729
+ okValue;
730
+ errValue;
731
+ extra;
732
+ constructor({ isOk, okValue, errValue, extra }) {
733
+ this.isOk = isOk;
734
+ this.okValue = okValue;
735
+ this.errValue = errValue;
736
+ this.extra = extra;
737
+ }
738
+ static Ok(args) {
739
+ return new _Result({ isOk: true, okValue: args.value, extra: args.extra });
740
+ }
741
+ static Err(args) {
742
+ return new _Result({ isOk: false, errValue: args.error, extra: args.extra });
743
+ }
744
+ /**
745
+ * You write a closure that either returns a T or throws, and we wrap it to return a Result<T>.
746
+ * Your closure is also given an `extra` dictionary to record additional context.
747
+ */
748
+ static capture({ task, recordsExecutionTimeMs = false }) {
749
+ const start = performance.now();
750
+ var extra = {};
751
+ var error;
752
+ var okValue;
753
+ try {
754
+ okValue = task(extra);
755
+ } catch (caught) {
756
+ error = caught;
757
+ } finally {
758
+ if (recordsExecutionTimeMs) {
759
+ extra["_sdk_execution_time_ms"] = performance.now() - start;
760
+ }
761
+ if (error !== void 0) {
762
+ return _Result.Err({ error, extra });
763
+ } else {
764
+ return _Result.Ok({ value: okValue, extra });
765
+ }
766
+ }
767
+ }
768
+ /**
769
+ * You write an async closure that either returns a T or throws, and we wrap it to return a Result<T>.
770
+ * Your closure is also given an `extra` dictionary to record additional context.
771
+ */
772
+ static async captureAsync({ task, recordsExecutionTimeMs = false }) {
773
+ var extra = {};
774
+ const start = performance.now();
775
+ var error;
776
+ var okValue;
777
+ try {
778
+ okValue = await task(extra);
779
+ } catch (caught) {
780
+ error = caught;
781
+ } finally {
782
+ if (recordsExecutionTimeMs) {
783
+ extra["_sdk_execution_time_ms"] = performance.now() - start;
784
+ }
785
+ if (error !== void 0) {
786
+ return _Result.Err({ error, extra });
787
+ } else {
788
+ return _Result.Ok({ value: okValue, extra });
789
+ }
790
+ }
791
+ }
792
+ unwrapOrThrow(tothrow) {
793
+ if (!this.isOk) throw tothrow;
794
+ return this.okValue;
795
+ }
796
+ unwrapErrOrThrow(tothrow) {
797
+ if (this.isOk) throw tothrow;
798
+ return this.errValue;
799
+ }
800
+ };
801
+
802
+ // src/ibe/otp_hmac_boneh_franklin_bls12381_short_pk.ts
723
803
  var DST_OTP = new TextEncoder().encode("BONEH_FRANKLIN_BLS12381_SHORT_PK/OTP");
724
804
  var DST_ID_HASH = new TextEncoder().encode("BONEH_FRANKLIN_BLS12381_SHORT_PK/HASH_ID_TO_CURVE");
725
805
  var DST_MAC = new TextEncoder().encode("BONEH_FRANKLIN_BLS12381_SHORT_PK/MAC");
@@ -853,6 +933,21 @@ function decrypt3(identityKey, ciphertext) {
853
933
  const plaintext = xorBytes(otp, ciphertext.symmetricCiph);
854
934
  return plaintext;
855
935
  }
936
+ function tryDecrypt(identityKey, ciphertext) {
937
+ const task = (_extra) => {
938
+ const seedElementGt = bls12_381.pairing(ciphertext.c0, identityKey.privatePointG2);
939
+ const seed = bls12381GtReprNobleToAptos(bls12_381.fields.Fp12.toBytes(seedElementGt));
940
+ const macKey = kdf(seed, DST_MAC, 32);
941
+ const macAnother = hmac_sha3_256(macKey, ciphertext.symmetricCiph);
942
+ if (bytesToHex4(ciphertext.mac) !== bytesToHex4(macAnother)) {
943
+ throw "decryption failed";
944
+ }
945
+ const otp = kdf(seed, DST_OTP, ciphertext.symmetricCiph.length);
946
+ const plaintext = xorBytes(otp, ciphertext.symmetricCiph);
947
+ return plaintext;
948
+ };
949
+ return Result.capture({ task, recordsExecutionTimeMs: true });
950
+ }
856
951
 
857
952
  // src/ibe/index.ts
858
953
  var SCHEME_OTP_HAMC_BONEH_FRANKLIN_BLS12381_SHORT_PK = 0;
@@ -1083,12 +1178,36 @@ function extract2(privateKey, id) {
1083
1178
  }
1084
1179
  throw new Error(`Unknown scheme: ${privateKey.scheme}`);
1085
1180
  }
1181
+ function tryExtract(privateKey, id) {
1182
+ const task = (extra) => {
1183
+ extra["scheme"] = privateKey.scheme;
1184
+ if (privateKey.scheme == SCHEME_OTP_HAMC_BONEH_FRANKLIN_BLS12381_SHORT_PK) {
1185
+ return new IdentityPrivateKey2(
1186
+ SCHEME_OTP_HAMC_BONEH_FRANKLIN_BLS12381_SHORT_PK,
1187
+ extract(privateKey.inner, id)
1188
+ );
1189
+ }
1190
+ throw `unknown scheme`;
1191
+ };
1192
+ return Result.capture({ task, recordsExecutionTimeMs: true });
1193
+ }
1086
1194
  function decrypt4(identityKey, ciphertext) {
1087
1195
  if (identityKey.scheme == SCHEME_OTP_HAMC_BONEH_FRANKLIN_BLS12381_SHORT_PK) {
1088
1196
  return decrypt3(identityKey.inner, ciphertext.inner);
1089
1197
  }
1090
1198
  throw new Error(`Unknown scheme: ${identityKey.scheme}`);
1091
1199
  }
1200
+ function tryDecrypt2(identityKey, ciphertext) {
1201
+ const task = (extra) => {
1202
+ extra["scheme"] = identityKey.scheme;
1203
+ if (identityKey.scheme == SCHEME_OTP_HAMC_BONEH_FRANKLIN_BLS12381_SHORT_PK) {
1204
+ const innerResult = tryDecrypt(identityKey.inner, ciphertext.inner);
1205
+ return innerResult.unwrapOrThrow("OtpHmacBonehFranklinBls12381ShortPK.tryDecrypt failed");
1206
+ }
1207
+ throw `unknown scheme`;
1208
+ };
1209
+ return Result.capture({ task, recordsExecutionTimeMs: true });
1210
+ }
1092
1211
 
1093
1212
  // src/sig/index.ts
1094
1213
  var sig_exports = {};
@@ -2155,7 +2274,8 @@ __export(sym_exports, {
2155
2274
  decrypt: () => decrypt6,
2156
2275
  encrypt: () => encrypt6,
2157
2276
  encryptWithRandomness: () => encryptWithRandomness6,
2158
- keygen: () => keygen8
2277
+ keygen: () => keygen8,
2278
+ tryDecrypt: () => tryDecrypt4
2159
2279
  });
2160
2280
  import { Deserializer as Deserializer9, Serializer as Serializer10 } from "@aptos-labs/ts-sdk";
2161
2281
  import { bytesToHex as bytesToHex9 } from "@noble/curves/utils";
@@ -2256,6 +2376,16 @@ function decrypt5(key, ciphertext) {
2256
2376
  return void 0;
2257
2377
  }
2258
2378
  }
2379
+ function tryDecrypt3(key, ciphertext) {
2380
+ const task = (_extra) => {
2381
+ const gcmInstance = gcm(key.inner, ciphertext.iv);
2382
+ const encryptedData = new Uint8Array(ciphertext.ct.length + ciphertext.tag.length);
2383
+ encryptedData.set(ciphertext.ct, 0);
2384
+ encryptedData.set(ciphertext.tag, ciphertext.ct.length);
2385
+ return gcmInstance.decrypt(encryptedData);
2386
+ };
2387
+ return Result.capture({ task, recordsExecutionTimeMs: true });
2388
+ }
2259
2389
 
2260
2390
  // src/sym/index.ts
2261
2391
  var SCHEME_AES256GCM = 0;
@@ -2365,12 +2495,24 @@ function decrypt6(key, ciphertext) {
2365
2495
  }
2366
2496
  throw new Error("Invalid scheme");
2367
2497
  }
2498
+ function tryDecrypt4(key, ciphertext) {
2499
+ const task = (extra) => {
2500
+ extra["scheme"] = key.scheme;
2501
+ if (key.scheme === SCHEME_AES256GCM) {
2502
+ const innerResult = tryDecrypt3(key.inner, ciphertext.inner);
2503
+ return innerResult.unwrapOrThrow("AES256GCM decryption failed");
2504
+ }
2505
+ throw `unknown scheme`;
2506
+ };
2507
+ return Result.capture({ task, recordsExecutionTimeMs: true });
2508
+ }
2368
2509
 
2369
2510
  // src/worker_config.ts
2370
2511
  var worker_config_exports = {};
2371
2512
  __export(worker_config_exports, {
2372
2513
  WorkerConfig: () => WorkerConfig,
2373
2514
  get: () => get,
2515
+ getAsync: () => getAsync,
2374
2516
  randWorker: () => randWorker,
2375
2517
  view: () => view
2376
2518
  });
@@ -2460,6 +2602,23 @@ async function get(workerEndpoint) {
2460
2602
  const hex = await response.text();
2461
2603
  return WorkerConfig.fromHex(hex);
2462
2604
  }
2605
+ async function getAsync(workerEndpoint) {
2606
+ const task = async (extra) => {
2607
+ const url = `${workerEndpoint}/config_bcs`;
2608
+ extra["url"] = url;
2609
+ const response = await fetch(url, {
2610
+ method: "GET"
2611
+ });
2612
+ extra["responseStatus"] = response.status;
2613
+ extra["responseStatusText"] = response.statusText;
2614
+ if (!response.ok) {
2615
+ throw `Failed to fetch worker config: ${response.status} ${response.statusText}`;
2616
+ }
2617
+ const hex = await response.text();
2618
+ return WorkerConfig.fromHex(hex);
2619
+ };
2620
+ return await Result.captureAsync({ task, recordsExecutionTimeMs: true });
2621
+ }
2463
2622
  function randWorker() {
2464
2623
  const addr = new AccountAddress(utils_exports.randBytes(32));
2465
2624
  const encDk = keygen2();
@@ -2486,16 +2645,16 @@ __export(threshold_ibe_exports, {
2486
2645
  EncryptionKey: () => EncryptionKey2,
2487
2646
  FullDecryptionDomain: () => FullDecryptionDomain,
2488
2647
  ProofOfPermission: () => ProofOfPermission3,
2489
- decrypt: () => decrypt7,
2490
2648
  encrypt: () => encrypt7,
2491
- verifyAndExtract: () => verifyAndExtract
2649
+ tryDecrypt: () => tryDecrypt5,
2650
+ tryExtract: () => tryExtract2
2492
2651
  });
2493
2652
  import { Deserializer as Deserializer13, Serializer as Serializer14 } from "@aptos-labs/ts-sdk";
2494
2653
  import { bytesToHex as bytesToHex13, hexToBytes as hexToBytes8 } from "@noble/hashes/utils";
2495
2654
  import * as SolanaSDK from "@solana/web3.js";
2496
2655
 
2497
2656
  // src/threshold-ibe/aptos.ts
2498
- import { AccountAddress as AccountAddress2, AnyPublicKey, AnySignature, Aptos as Aptos2, AptosConfig as AptosConfig2, Deserializer as Deserializer11, Ed25519PublicKey, Ed25519Signature, FederatedKeylessPublicKey, KeylessPublicKey, KeylessSignature, MultiEd25519PublicKey, MultiEd25519Signature, MultiKey, MultiKeySignature, Network as Network3, Serializer as Serializer12 } from "@aptos-labs/ts-sdk";
2657
+ import { AccountAddress as AccountAddress2, AccountPublicKey, AnyPublicKey, AnySignature, Aptos as Aptos2, AptosConfig as AptosConfig2, Deserializer as Deserializer11, Ed25519PublicKey, Ed25519Signature, FederatedKeylessPublicKey, KeylessPublicKey, KeylessSignature, MultiEd25519PublicKey, MultiEd25519Signature, MultiKey, MultiKeySignature, Network as Network3, Serializer as Serializer12 } from "@aptos-labs/ts-sdk";
2499
2658
  import { bytesToHex as bytesToHex11, hexToBytes as hexToBytes6 } from "@noble/hashes/utils";
2500
2659
  var ContractID = class _ContractID {
2501
2660
  chainId;
@@ -2676,64 +2835,76 @@ var ProofOfPermission = class _ProofOfPermission {
2676
2835
  }
2677
2836
  };
2678
2837
  async function verifyPermission({ fullDecryptionDomain, proof }) {
2679
- const aptos = createAptos(getChainNameFromChainId(fullDecryptionDomain.getAptosContractID().chainId));
2680
- const taskVerifySig = async () => {
2838
+ const task = async (extra) => {
2839
+ const aptos = createAptos(getChainNameFromChainId(fullDecryptionDomain.getAptosContractID().chainId));
2840
+ const [verifySigResult, checkAuthKeyResult, checkPermissionResult] = await Promise.all([
2841
+ verifySig({ aptos, fullDecryptionDomain, proof }),
2842
+ checkAuthKey({ aptos, userAddr: proof.userAddr, publicKey: proof.publicKey }),
2843
+ checkPermission({ aptos, fullDecryptionDomain, proof })
2844
+ ]);
2845
+ extra["verifySigResult"] = verifySigResult;
2846
+ extra["checkAuthKeyResult"] = checkAuthKeyResult;
2847
+ extra["checkPermissionResult"] = checkPermissionResult;
2848
+ if (!verifySigResult.isOk || !checkAuthKeyResult.isOk || !checkPermissionResult.isOk) {
2849
+ throw "one or more sub-checks failed";
2850
+ }
2851
+ };
2852
+ return await Result.captureAsync({ task, recordsExecutionTimeMs: true });
2853
+ }
2854
+ async function verifySig({ aptos, fullDecryptionDomain, proof }) {
2855
+ const task = async (extra) => {
2681
2856
  const msgToSign = fullDecryptionDomain.toPrettyMessage();
2682
2857
  const msgToSignHex = bytesToHex11(new TextEncoder().encode(msgToSign));
2683
- const fullMessageFromPetra = proof.fullMessage.includes(msgToSign);
2684
- const fullMessageFromAptosConnect = proof.fullMessage.includes(msgToSignHex);
2685
- if (!fullMessageFromPetra && !fullMessageFromAptosConnect) return false;
2686
- try {
2687
- return await proof.publicKey.verifySignatureAsync({
2688
- aptosConfig: aptos.config,
2689
- message: proof.fullMessage,
2690
- signature: proof.signature
2691
- });
2692
- } catch (error) {
2693
- return false;
2694
- }
2858
+ const fullMessageSeemsFromPetra = proof.fullMessage.includes(msgToSign);
2859
+ const fullMessageSeemsFromAptosConnect = proof.fullMessage.includes(msgToSignHex);
2860
+ extra["msgToSign"] = msgToSign;
2861
+ extra["msgToSignHex"] = msgToSignHex;
2862
+ extra["fullMessageSeemsFromPetra"] = fullMessageSeemsFromPetra;
2863
+ extra["fullMessageSeemsFromAptosConnect"] = fullMessageSeemsFromAptosConnect;
2864
+ if (!fullMessageSeemsFromPetra && !fullMessageSeemsFromAptosConnect) throw "fullMessage does not contain fullDecryptionDomain or its hex";
2865
+ const sigValid = await proof.publicKey.verifySignatureAsync({
2866
+ aptosConfig: aptos.config,
2867
+ message: proof.fullMessage,
2868
+ signature: proof.signature
2869
+ });
2870
+ if (!sigValid) throw "verifySignatureAsync failed";
2695
2871
  };
2696
- const taskCheckAuthKey = async () => {
2697
- try {
2698
- const onChainAuthKeyBytes = await getAccountAuthKeyBytes(aptos, proof.userAddr);
2699
- const userAuthKeyBytes = proof.publicKey.authKey().bcsToBytes();
2700
- const onChainHex = bytesToHex11(onChainAuthKeyBytes);
2701
- const userHex = bytesToHex11(userAuthKeyBytes);
2702
- console.log(`onChainHex: ${onChainHex}`);
2703
- console.log(`userHex : ${userHex}`);
2704
- return onChainHex === userHex;
2705
- } catch (error) {
2706
- return false;
2872
+ return await Result.captureAsync({ task, recordsExecutionTimeMs: true });
2873
+ }
2874
+ async function checkAuthKey({ aptos, userAddr, publicKey }) {
2875
+ const task = async (extra) => {
2876
+ if (!(publicKey instanceof AccountPublicKey)) {
2877
+ throw "publicKey is not an AccountPublicKey";
2707
2878
  }
2879
+ const onChainAuthKeyBytes = await getAccountAuthKeyBytes(aptos, userAddr);
2880
+ const userAuthKeyBytes = publicKey.authKey().bcsToBytes();
2881
+ const onChainHex = bytesToHex11(onChainAuthKeyBytes);
2882
+ const userHex = bytesToHex11(userAuthKeyBytes);
2883
+ extra["onChainHex"] = onChainHex;
2884
+ extra["userHex"] = userHex;
2885
+ if (onChainHex !== userHex) throw "on-chain auth key does not match user auth key";
2708
2886
  };
2709
- const taskCheckPermission = async () => {
2887
+ return await Result.captureAsync({ task, recordsExecutionTimeMs: true });
2888
+ }
2889
+ async function checkPermission({ aptos, fullDecryptionDomain, proof }) {
2890
+ const task = async (extra) => {
2710
2891
  const contractId = fullDecryptionDomain.getAptosContractID();
2711
- try {
2712
- const userIsPermittedMoveVal = await view2(
2713
- aptos,
2714
- `${contractId.moduleAddr.toStringLong()}::${contractId.moduleName}::${contractId.functionName}`,
2715
- [],
2716
- [proof.userAddr, fullDecryptionDomain.domain]
2717
- );
2718
- return userIsPermittedMoveVal?.toString() === "true";
2719
- } catch (error) {
2720
- return false;
2892
+ const viewFunctionInvocationResult = await view2({
2893
+ aptos,
2894
+ func: `${contractId.moduleAddr.toStringLong()}::${contractId.moduleName}::${contractId.functionName}`,
2895
+ typeArguments: [],
2896
+ functionArguments: [proof.userAddr, fullDecryptionDomain.domain]
2897
+ });
2898
+ extra["viewFunctionInvocationResult"] = viewFunctionInvocationResult;
2899
+ if (!viewFunctionInvocationResult.isOk) {
2900
+ throw "view function invocation failed";
2901
+ }
2902
+ const returnedMoveValue = viewFunctionInvocationResult.okValue;
2903
+ if (returnedMoveValue?.toString() !== "true") {
2904
+ throw "access control contract return value is not true";
2721
2905
  }
2722
2906
  };
2723
- const [sigIsValid, authKeyMatches, userIsPermitted] = await Promise.all([
2724
- taskVerifySig(),
2725
- taskCheckAuthKey(),
2726
- taskCheckPermission()
2727
- ]);
2728
- if (!sigIsValid) {
2729
- throw new Error("Signature invalid.");
2730
- }
2731
- if (!authKeyMatches) {
2732
- throw new Error("Authentication key mismatch: on-chain key does not match provided public key.");
2733
- }
2734
- if (!userIsPermitted) {
2735
- throw new Error("Permission denied.");
2736
- }
2907
+ return await Result.captureAsync({ task, recordsExecutionTimeMs: true });
2737
2908
  }
2738
2909
  function getChainNameFromChainId(chainId) {
2739
2910
  if (chainId === 1) {
@@ -2770,18 +2941,22 @@ async function getAccountAuthKeyBytes(aptos, address) {
2770
2941
  const accountInfo = await aptos.getAccountInfo({ accountAddress: address });
2771
2942
  return hexToBytes6(accountInfo.authentication_key.replace("0x", ""));
2772
2943
  }
2773
- async function view2(aptos, func, typeArguments, functionArguments) {
2774
- const result = await aptos.view({
2775
- payload: {
2776
- function: func,
2777
- typeArguments,
2778
- functionArguments
2944
+ async function view2({ aptos, func, typeArguments, functionArguments }) {
2945
+ const task = async (extra) => {
2946
+ const returnedMoveValues = await aptos.view({
2947
+ payload: {
2948
+ function: func,
2949
+ typeArguments,
2950
+ functionArguments
2951
+ }
2952
+ });
2953
+ extra["returnedMoveValues"] = returnedMoveValues;
2954
+ if (returnedMoveValues.length === 0) {
2955
+ throw `aptos.view returned an empty list`;
2779
2956
  }
2780
- });
2781
- if (result.length === 0) {
2782
- throw new Error(`View function returned empty result`);
2783
- }
2784
- return result[0];
2957
+ return returnedMoveValues[0];
2958
+ };
2959
+ return await Result.captureAsync({ task, recordsExecutionTimeMs: true });
2785
2960
  }
2786
2961
 
2787
2962
  // src/threshold-ibe/solana.ts
@@ -2882,76 +3057,107 @@ var ProofOfPermission2 = class _ProofOfPermission {
2882
3057
  }
2883
3058
  };
2884
3059
  async function verifyPermission2({ fullDecryptionDomain, proof }) {
2885
- const txn = proof.inner;
2886
- assertTransactionValid({ txn, fullDecryptionDomain });
2887
- await assertTransactionSimulationPasses(txn, fullDecryptionDomain.getSolanaContractID().knownChainName);
2888
- }
2889
- function assertTransactionValid({ txn, fullDecryptionDomain }) {
2890
- let instructions;
2891
- if (txn instanceof VersionedTransaction) {
2892
- const message = txn.message;
2893
- instructions = message.compiledInstructions.map((ix) => {
2894
- if (ix.programIdIndex >= message.staticAccountKeys.length) {
2895
- 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.`);
2896
- }
2897
- const programId = message.staticAccountKeys[ix.programIdIndex];
2898
- return { programId, data: Buffer.from(ix.data) };
2899
- });
2900
- } else {
2901
- instructions = txn.instructions.map((ix) => ({
2902
- programId: ix.programId,
2903
- data: Buffer.from(ix.data)
2904
- }));
2905
- }
2906
- if (instructions.length !== 1) {
2907
- throw new Error(`transaction must contain exactly 1 instruction, found ${instructions.length}`);
2908
- }
2909
- const instruction = instructions[0];
2910
- if (!instruction.programId.equals(fullDecryptionDomain.getSolanaContractID().programId)) {
2911
- throw new Error(`transaction instruction program ID (${instruction.programId.toString()}) does not match contract program ID`);
2912
- }
2913
- const instructionData = instruction.data;
2914
- if (instructionData.length < 12) {
2915
- throw new Error("instruction data too short (must be at least 12 bytes: 8-byte discriminator + 4-byte Vec length)");
2916
- }
2917
- const paramData = instructionData.slice(8);
2918
- const vecLength = paramData.readUInt32LE(0);
2919
- if (paramData.length < 4 + vecLength) {
2920
- throw new Error(`instruction data incomplete: expected ${4 + vecLength} bytes after discriminator, found ${paramData.length}`);
2921
- }
2922
- const fullBlobNameBytes = paramData.slice(4, 4 + vecLength);
2923
- const expectedParamDataLength = 4 + vecLength;
2924
- if (paramData.length > expectedParamDataLength) {
2925
- throw new Error(`instruction data has extra bytes: expected exactly ${expectedParamDataLength} bytes after discriminator, found ${paramData.length}`);
3060
+ var extra = {};
3061
+ try {
3062
+ const txn = proof.inner;
3063
+ const validateTxnResult = validateTxn({ txn, fullDecryptionDomain });
3064
+ if (!validateTxnResult.isOk) {
3065
+ extra["causedBy"] = validateTxnResult.extra;
3066
+ throw "transaction is invalid";
3067
+ }
3068
+ const simulationResult = await assertTransactionSimulationPasses(txn, fullDecryptionDomain.getSolanaContractID().knownChainName);
3069
+ if (!simulationResult.isOk) {
3070
+ extra["causedBy"] = simulationResult.extra;
3071
+ throw "transaction simulation failed";
3072
+ }
3073
+ return Result.Ok({ value: void 0, extra });
3074
+ } catch (error) {
3075
+ return Result.Err({ error, extra });
2926
3076
  }
2927
- if (bytesToHex12(fullBlobNameBytes) !== fullDecryptionDomain.toHex()) {
2928
- throw new Error(`domain mismatch: instruction parameter does not match decryptionContext.domain`);
3077
+ }
3078
+ function validateTxn({ txn, fullDecryptionDomain }) {
3079
+ try {
3080
+ let instructions;
3081
+ if (txn instanceof VersionedTransaction) {
3082
+ const message = txn.message;
3083
+ instructions = message.compiledInstructions.map((ix) => {
3084
+ if (ix.programIdIndex >= message.staticAccountKeys.length) {
3085
+ 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)`;
3086
+ }
3087
+ const programId = message.staticAccountKeys[ix.programIdIndex];
3088
+ return { programId, data: Buffer.from(ix.data) };
3089
+ });
3090
+ } else {
3091
+ instructions = txn.instructions.map((ix) => ({
3092
+ programId: ix.programId,
3093
+ data: Buffer.from(ix.data)
3094
+ }));
3095
+ }
3096
+ if (instructions.length !== 1) throw `transaction must contain exactly 1 instruction`;
3097
+ const instruction = instructions[0];
3098
+ if (!instruction.programId.equals(fullDecryptionDomain.getSolanaContractID().programId)) {
3099
+ throw `transaction instruction program ID does not match contract program ID`;
3100
+ }
3101
+ const instructionData = instruction.data;
3102
+ if (instructionData.length < 12) {
3103
+ throw "instruction data too short";
3104
+ }
3105
+ const paramData = instructionData.slice(8);
3106
+ const vecLength = paramData.readUInt32LE(0);
3107
+ if (paramData.length < 4 + vecLength) throw `instruction data incomplete`;
3108
+ const domainAsTxnParam = paramData.slice(4, 4 + vecLength);
3109
+ const expectedParamDataLength = 4 + vecLength;
3110
+ if (paramData.length > expectedParamDataLength) {
3111
+ throw `instruction data has extra bytes`;
3112
+ }
3113
+ if (bytesToHex12(domainAsTxnParam) !== bytesToHex12(fullDecryptionDomain.domain)) {
3114
+ throw `instruction parameter does not match decryptionContext.domain`;
3115
+ }
3116
+ return Result.Ok({ value: void 0 });
3117
+ } catch (error) {
3118
+ return Result.Err({ error });
2929
3119
  }
2930
3120
  }
2931
3121
  async function assertTransactionSimulationPasses(txn, chainName) {
2932
- let rpcUrl;
2933
- if (chainName === "localnet" || chainName === "localhost") {
2934
- rpcUrl = "http://127.0.0.1:8899";
2935
- } else if (chainName === "devnet") {
2936
- rpcUrl = "https://api.devnet.solana.com";
2937
- } else if (chainName === "testnet") {
2938
- rpcUrl = "https://api.testnet.solana.com";
2939
- } else if (chainName === "mainnet-beta") {
2940
- rpcUrl = "https://api.mainnet-beta.solana.com";
2941
- } else {
2942
- throw new Error(`Unknown chain name: ${chainName}`);
2943
- }
2944
- const connection = new Connection(rpcUrl, "confirmed");
2945
- let simulation;
2946
- if (txn instanceof VersionedTransaction) {
2947
- simulation = await connection.simulateTransaction(txn, {
2948
- sigVerify: true
2949
- });
2950
- } else {
2951
- simulation = await connection.simulateTransaction(txn);
2952
- }
2953
- if (simulation.value.err) {
2954
- throw new Error(`transaction simulation failed: ${JSON.stringify(simulation.value.err)}`);
3122
+ var extra = {};
3123
+ var error;
3124
+ const start = performance.now();
3125
+ try {
3126
+ let rpcUrl;
3127
+ if (chainName === "localnet" || chainName === "localhost") {
3128
+ rpcUrl = "http://127.0.0.1:8899";
3129
+ } else if (chainName === "devnet") {
3130
+ rpcUrl = "https://api.devnet.solana.com";
3131
+ } else if (chainName === "testnet") {
3132
+ rpcUrl = "https://api.testnet.solana.com";
3133
+ } else if (chainName === "mainnet-beta") {
3134
+ rpcUrl = "https://api.mainnet-beta.solana.com";
3135
+ } else {
3136
+ extra["chainName"] = chainName;
3137
+ throw `unsupported chain name`;
3138
+ }
3139
+ const connection = new Connection(rpcUrl, "confirmed");
3140
+ let simulation;
3141
+ if (txn instanceof VersionedTransaction) {
3142
+ simulation = await connection.simulateTransaction(txn, {
3143
+ sigVerify: true
3144
+ });
3145
+ } else {
3146
+ simulation = await connection.simulateTransaction(txn);
3147
+ }
3148
+ if (simulation.value.err) {
3149
+ extra["simulationError"] = simulation.value.err;
3150
+ throw `transaction simulation failed`;
3151
+ }
3152
+ } catch (caught) {
3153
+ error = caught;
3154
+ } finally {
3155
+ extra["executionTimeMs"] = performance.now() - start;
3156
+ if (error !== void 0) {
3157
+ return Result.Err({ error, extra });
3158
+ } else {
3159
+ return Result.Ok({ value: void 0, extra });
3160
+ }
2955
3161
  }
2956
3162
  }
2957
3163
 
@@ -3057,11 +3263,16 @@ var EncryptionKey2 = class _EncryptionKey {
3057
3263
  this.ibeMpks = ibeMpks;
3058
3264
  }
3059
3265
  static async fetch({ committee }) {
3060
- const ibeMpks = await Promise.all(committee.workerEndpoints.map(async (endpoint) => {
3061
- const config = await worker_config_exports.get(endpoint);
3062
- return config.ibeMpk;
3063
- }));
3064
- return new _EncryptionKey({ ibeMpks });
3266
+ const task = async (extra) => {
3267
+ const workerConfigGetResults = await Promise.all(committee.workerEndpoints.map(async (endpoint) => {
3268
+ return worker_config_exports.getAsync(endpoint);
3269
+ }));
3270
+ extra["workerConfigGetResults"] = workerConfigGetResults;
3271
+ if (workerConfigGetResults.some((result) => !result.isOk)) throw "failed to get all worker configs";
3272
+ const ibeMpks = workerConfigGetResults.map((result) => result.okValue.ibeMpk);
3273
+ return new _EncryptionKey({ ibeMpks });
3274
+ };
3275
+ return await Result.captureAsync({ task, recordsExecutionTimeMs: true });
3065
3276
  }
3066
3277
  };
3067
3278
  var DecryptionKey2 = class _DecryptionKey {
@@ -3070,43 +3281,43 @@ var DecryptionKey2 = class _DecryptionKey {
3070
3281
  this.ibeDecryptionKeys = ibeDecryptionKeys;
3071
3282
  }
3072
3283
  static async fetch({ committee, contractId, domain, proof }) {
3073
- const decKeyLoadResults = await Promise.all(committee.workerEndpoints.map(async (workerEndpoint) => {
3074
- const task = WorkerTask.newThresholdIbeDecryptionKey({ committee, contractId, domain, proof });
3284
+ const task = async (extra) => {
3285
+ extra["committee"] = committee;
3286
+ const decKeyLoadResults = await Promise.all(committee.workerEndpoints.map(async (_workerEndpoint, index) => {
3287
+ return _DecryptionKey.fetchDecKeyShare({ committee, contractId, domain, proof, index });
3288
+ }));
3289
+ extra["decKeyLoadResults"] = decKeyLoadResults;
3290
+ const numSharesCollected = decKeyLoadResults.filter((loadResult) => loadResult.isOk).length;
3291
+ if (numSharesCollected < committee.threshold) throw `failed to collect enough shares`;
3292
+ const decKeyShares = decKeyLoadResults.map((loadResult) => loadResult.okValue ?? null);
3293
+ return new _DecryptionKey(decKeyShares);
3294
+ };
3295
+ return Result.captureAsync({ task, recordsExecutionTimeMs: true });
3296
+ }
3297
+ static async fetchDecKeyShare({ committee, contractId, domain, proof, index }) {
3298
+ const task = async (extra) => {
3299
+ const targetWorkerEndpoint = committee.workerEndpoints[index];
3300
+ const task2 = WorkerTask.newThresholdIbeDecryptionKey({ committee, contractId, domain, proof });
3075
3301
  const controller = new AbortController();
3076
3302
  const timeoutId = setTimeout(() => controller.abort(), 5e3);
3077
3303
  var response = null;
3078
3304
  try {
3079
- response = await fetch(workerEndpoint, {
3305
+ response = await fetch(targetWorkerEndpoint, {
3080
3306
  method: "POST",
3081
- body: task.toHex(),
3307
+ body: task2.toHex(),
3082
3308
  signal: controller.signal
3083
3309
  });
3084
3310
  } catch (error) {
3085
3311
  clearTimeout(timeoutId);
3086
3312
  }
3087
- if (response == null) {
3088
- return new WorkerTimedOut();
3089
- }
3313
+ if (response == null) throw "worker is not responding";
3090
3314
  const responseBody = await response.text();
3091
- if (response.status !== 200) {
3092
- return new WorkerRejected(response.status, responseBody);
3093
- }
3094
- try {
3095
- return IdentityPrivateKey2.fromHex(responseBody);
3096
- } catch (error) {
3097
- return new CouldNotParseDecryptionKey(responseBody);
3098
- }
3099
- }));
3100
- const numSharesCollected = decKeyLoadResults.filter((loadResult) => loadResult instanceof IdentityPrivateKey2).length;
3101
- if (numSharesCollected < committee.threshold) {
3102
- const workerResults = committee.workerEndpoints.map((workerEndpoint, i) => {
3103
- const result = decKeyLoadResults[i];
3104
- return `${workerEndpoint}: ${result instanceof IdentityPrivateKey2 ? "Success" : result.toDisplayString()}`;
3105
- }).join(", ");
3106
- throw new Error(`Failed to collect enough shares to decrypt. Collected ${numSharesCollected} shares, but needed ${committee.threshold} shares. Worker results: ${workerResults}`);
3107
- }
3108
- const decKeys = decKeyLoadResults.map((loadResult) => loadResult instanceof IdentityPrivateKey2 ? loadResult : null);
3109
- return new _DecryptionKey(decKeys);
3315
+ extra["workerResponseStatus"] = response.status;
3316
+ extra["workerResponseBody"] = responseBody;
3317
+ if (response.status !== 200) throw `worker rejected: ${response.status}`;
3318
+ return IdentityPrivateKey2.fromHex(responseBody);
3319
+ };
3320
+ return Result.captureAsync({ task, recordsExecutionTimeMs: true });
3110
3321
  }
3111
3322
  };
3112
3323
  var Ciphertext7 = class _Ciphertext {
@@ -3287,56 +3498,48 @@ function encrypt7({ committee, encryptionKey, contractId, domain, plaintext }) {
3287
3498
  const ibeCiphs = symmKeyShares.map((share, idx) => encrypt4(encryptionKey.ibeMpks[idx], fullDecryptionDomain.toBytes(), share.payload));
3288
3499
  return { fullDecryptionDomain, ciphertext: new Ciphertext7(symmCiph, ibeCiphs) };
3289
3500
  }
3290
- function decrypt7({ decryptionKey, ciphertext }) {
3291
- if (decryptionKey.ibeDecryptionKeys.length !== ciphertext.ibeCiphs.length) {
3292
- throw new Error("decryptionKey.ibeDecryptionKeys.length !== ciphertext.ibeCiphs.length");
3293
- }
3294
- const symmKeyShares = ciphertext.ibeCiphs.map((ibeCiph, idx) => {
3295
- if (decryptionKey.ibeDecryptionKeys[idx] == null) return null;
3296
- const sharePayload = decrypt4(decryptionKey.ibeDecryptionKeys[idx], ibeCiph);
3297
- if (sharePayload == null) return null;
3298
- return new Share(idx + 1, sharePayload);
3299
- }).filter((share) => share != null);
3300
- const symmKeyBytes = combine(symmKeyShares);
3301
- const symmKey = Key2.fromBytes(symmKeyBytes);
3302
- return decrypt6(symmKey, ciphertext.aesCiph);
3501
+ function tryDecrypt5({ decryptionKey, ciphertext }) {
3502
+ const task = (extra) => {
3503
+ extra["numIbeDecryptionKeys"] = decryptionKey.ibeDecryptionKeys.length;
3504
+ extra["numIbeCiphs"] = ciphertext.ibeCiphs.length;
3505
+ if (decryptionKey.ibeDecryptionKeys.length !== ciphertext.ibeCiphs.length) {
3506
+ throw "decryption key share count does not match ciphertext share count";
3507
+ }
3508
+ const unfilteredSymmKeyShares = ciphertext.ibeCiphs.map((ibeCiph, idx) => {
3509
+ if (decryptionKey.ibeDecryptionKeys[idx] == null) return null;
3510
+ const maybeSharePayload = tryDecrypt2(decryptionKey.ibeDecryptionKeys[idx], ibeCiph);
3511
+ if (!maybeSharePayload.isOk) return null;
3512
+ return new Share(idx + 1, maybeSharePayload.okValue);
3513
+ });
3514
+ extra["symmKeySharePresences"] = unfilteredSymmKeyShares.map((share) => share != null);
3515
+ const symmKeyShares = unfilteredSymmKeyShares.filter((share) => share != null);
3516
+ const symmKeyBytes = combine(symmKeyShares);
3517
+ const symmKey = Key2.fromBytes(symmKeyBytes);
3518
+ const symDecResult = tryDecrypt4(symmKey, ciphertext.aesCiph);
3519
+ return symDecResult.unwrapOrThrow("symmetric decryption failed");
3520
+ };
3521
+ return Result.capture({ task, recordsExecutionTimeMs: true });
3303
3522
  }
3304
- async function verifyAndExtract({ ibeMsk, committee, contractId, domain, proof }) {
3305
- const decryptionContext = new FullDecryptionDomain({ committee, contractId, domain });
3306
- if (contractId.scheme == ContractID3.SCHEME_APTOS && proof.scheme == ProofOfPermission3.SCHEME_APTOS) {
3307
- await verifyPermission({ fullDecryptionDomain: decryptionContext, proof: proof.inner });
3308
- } else if (contractId.scheme == ContractID3.SCHEME_SOLANA && proof.scheme == ProofOfPermission3.SCHEME_SOLANA) {
3309
- await verifyPermission2({ fullDecryptionDomain: decryptionContext, proof: proof.inner });
3310
- } else {
3311
- throw new Error(`Unknown scheme: contractId=${contractId.scheme}, proof=${proof.scheme}`);
3312
- }
3313
- return extract2(ibeMsk, decryptionContext.toBytes());
3523
+ async function tryExtract2({ ibeMsk, committee, contractId, domain, proof }) {
3524
+ const task = async (extra) => {
3525
+ extra["contractIdScheme"] = contractId.scheme;
3526
+ extra["proofScheme"] = proof.scheme;
3527
+ const decryptionContext = new FullDecryptionDomain({ committee, contractId, domain });
3528
+ if (contractId.scheme == ContractID3.SCHEME_APTOS && proof.scheme == ProofOfPermission3.SCHEME_APTOS) {
3529
+ const aptosResult = await verifyPermission({ fullDecryptionDomain: decryptionContext, proof: proof.inner });
3530
+ extra["verifyAptosResult"] = aptosResult;
3531
+ if (!aptosResult.isOk) throw "aptos verification failed";
3532
+ } else if (contractId.scheme == ContractID3.SCHEME_SOLANA && proof.scheme == ProofOfPermission3.SCHEME_SOLANA) {
3533
+ const solanaResult = await verifyPermission2({ fullDecryptionDomain: decryptionContext, proof: proof.inner });
3534
+ extra["verifySolanaResult"] = solanaResult;
3535
+ if (!solanaResult.isOk) throw "solana verification failed";
3536
+ } else {
3537
+ throw "unsupported scheme combination";
3538
+ }
3539
+ return extract2(ibeMsk, decryptionContext.toBytes());
3540
+ };
3541
+ return Result.captureAsync({ task, recordsExecutionTimeMs: true });
3314
3542
  }
3315
- var WorkerTimedOut = class {
3316
- toDisplayString() {
3317
- return "Timed out";
3318
- }
3319
- };
3320
- var WorkerRejected = class {
3321
- statusCode;
3322
- responseBody;
3323
- constructor(statusCode, responseBody) {
3324
- this.statusCode = statusCode;
3325
- this.responseBody = responseBody;
3326
- }
3327
- toDisplayString() {
3328
- return `Rejected: ${this.statusCode} ${this.responseBody}`;
3329
- }
3330
- };
3331
- var CouldNotParseDecryptionKey = class {
3332
- originalHex;
3333
- constructor(originalHex) {
3334
- this.originalHex = originalHex;
3335
- }
3336
- toDisplayString() {
3337
- return `Could not parse decryption key: ${this.originalHex}`;
3338
- }
3339
- };
3340
3543
 
3341
3544
  // src/worker_task.ts
3342
3545
  var TYPE_SILENT_SETUP_DECRYPTION_KEY = 5;
@@ -3566,16 +3769,16 @@ var DecryptionContext = class _DecryptionContext {
3566
3769
  clearTimeout(timeoutId);
3567
3770
  }
3568
3771
  if (response == null) {
3569
- return new WorkerTimedOut2();
3772
+ return new WorkerTimedOut();
3570
3773
  }
3571
3774
  const responseBody = await response.text();
3572
3775
  if (response.status !== 200) {
3573
- return new WorkerRejected2(response.status, responseBody);
3776
+ return new WorkerRejected(response.status, responseBody);
3574
3777
  }
3575
3778
  try {
3576
3779
  return IdentityPrivateKey2.fromHex(responseBody);
3577
3780
  } catch (error) {
3578
- return new CouldNotParseDecryptionKey2(responseBody);
3781
+ return new CouldNotParseDecryptionKey(responseBody);
3579
3782
  }
3580
3783
  }));
3581
3784
  const numSharesCollected = decKeyLoadResults.filter((loadResult) => loadResult instanceof IdentityPrivateKey2).length;
@@ -3681,12 +3884,12 @@ var Decryptor = class {
3681
3884
  return decrypt6(symmKey, ciphertext.aesCiph);
3682
3885
  }
3683
3886
  };
3684
- var WorkerTimedOut2 = class {
3887
+ var WorkerTimedOut = class {
3685
3888
  toDisplayString() {
3686
3889
  return "Timed out";
3687
3890
  }
3688
3891
  };
3689
- var WorkerRejected2 = class {
3892
+ var WorkerRejected = class {
3690
3893
  statusCode;
3691
3894
  responseBody;
3692
3895
  constructor(statusCode, responseBody) {
@@ -3697,7 +3900,7 @@ var WorkerRejected2 = class {
3697
3900
  return `Rejected: ${this.statusCode} ${this.responseBody}`;
3698
3901
  }
3699
3902
  };
3700
- var CouldNotParseDecryptionKey2 = class {
3903
+ var CouldNotParseDecryptionKey = class {
3701
3904
  originalHex;
3702
3905
  constructor(originalHex) {
3703
3906
  this.originalHex = originalHex;
@@ -4261,16 +4464,16 @@ var DecryptionContext2 = class _DecryptionContext {
4261
4464
  clearTimeout(timeoutId);
4262
4465
  }
4263
4466
  if (response == null) {
4264
- return new WorkerTimedOut3();
4467
+ return new WorkerTimedOut2();
4265
4468
  }
4266
4469
  const responseBody = await response.text();
4267
4470
  if (response.status !== 200) {
4268
- return new WorkerRejected3(response.status, responseBody);
4471
+ return new WorkerRejected2(response.status, responseBody);
4269
4472
  }
4270
4473
  try {
4271
4474
  return IdentityPrivateKey2.fromHex(responseBody);
4272
4475
  } catch (error) {
4273
- return new CouldNotParseDecryptionKey3(responseBody);
4476
+ return new CouldNotParseDecryptionKey2(responseBody);
4274
4477
  }
4275
4478
  }));
4276
4479
  const numSharesCollected = decKeyLoadResults.filter((loadResult) => loadResult instanceof IdentityPrivateKey2).length;
@@ -4375,12 +4578,12 @@ var Decryptor2 = class {
4375
4578
  return decrypt6(symmKey, ciphertext.aesCiph);
4376
4579
  }
4377
4580
  };
4378
- var WorkerTimedOut3 = class {
4581
+ var WorkerTimedOut2 = class {
4379
4582
  toDisplayString() {
4380
4583
  return "Timed out";
4381
4584
  }
4382
4585
  };
4383
- var WorkerRejected3 = class {
4586
+ var WorkerRejected2 = class {
4384
4587
  statusCode;
4385
4588
  responseBody;
4386
4589
  constructor(statusCode, responseBody) {
@@ -4391,7 +4594,7 @@ var WorkerRejected3 = class {
4391
4594
  return `Rejected: ${this.statusCode} ${this.responseBody}`;
4392
4595
  }
4393
4596
  };
4394
- var CouldNotParseDecryptionKey3 = class {
4597
+ var CouldNotParseDecryptionKey2 = class {
4395
4598
  originalHex;
4396
4599
  constructor(originalHex) {
4397
4600
  this.originalHex = originalHex;
@@ -4437,6 +4640,7 @@ export {
4437
4640
  enc_exports as Enc,
4438
4641
  group_exports as Group,
4439
4642
  ibe_exports as IBE,
4643
+ Result,
4440
4644
  sig_exports as Sig,
4441
4645
  silent_setup_encryption_exports as SilentSetupEncryption,
4442
4646
  silent_setup_encryption_xchain_exports as SilentSetupEncryptionXChain,