@cofhe/sdk 1.1.0-alpha.3 → 1.1.0-alpha.4

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/dist/node.cjs CHANGED
@@ -2332,6 +2332,8 @@ var CofheConfigSchema = zod.z.object({
2332
2332
  supportedChains: zod.z.array(zod.z.custom()),
2333
2333
  /** Default acp expiration in seconds, default is 30 days */
2334
2334
  defaultACPExpiration: zod.z.number().optional().default(60 * 60 * 24 * 30),
2335
+ /** Decrypts sent to the threshold network at a time per client; `Infinity` disables the cap (default 8) */
2336
+ decryptConcurrency: zod.z.union([zod.z.number().min(1), zod.z.literal(Infinity)]).optional().default(8),
2335
2337
  /** Storage method for fhe keys (defaults to indexedDB on web, filesystem on node) */
2336
2338
  fheKeyStorage: zod.z.object({
2337
2339
  getItem: zod.z.custom((val) => typeof val === "function", {
@@ -3874,6 +3876,32 @@ function throwIfSubmitRetryTimedOut(params) {
3874
3876
  });
3875
3877
  }
3876
3878
  }
3879
+ function decryptAbortedError(operationLabel, signal) {
3880
+ return new CofheError({
3881
+ code: "ABORTED" /* Aborted */,
3882
+ message: `${operationLabel} aborted by the caller`,
3883
+ cause: signal.reason instanceof Error ? signal.reason : void 0
3884
+ });
3885
+ }
3886
+ function throwIfAborted(signal, operationLabel) {
3887
+ if (signal?.aborted)
3888
+ throw decryptAbortedError(operationLabel, signal);
3889
+ }
3890
+ function sleepUnlessAborted(ms, signal, operationLabel) {
3891
+ return new Promise((resolve, reject) => {
3892
+ if (signal?.aborted)
3893
+ return reject(decryptAbortedError(operationLabel, signal));
3894
+ const onAbort = () => {
3895
+ clearTimeout(timer);
3896
+ reject(decryptAbortedError(operationLabel, signal));
3897
+ };
3898
+ const timer = setTimeout(() => {
3899
+ signal?.removeEventListener("abort", onAbort);
3900
+ resolve();
3901
+ }, ms);
3902
+ signal?.addEventListener("abort", onAbort, { once: true });
3903
+ });
3904
+ }
3877
3905
  function normalizeTnSignature(signature) {
3878
3906
  if (typeof signature !== "string") {
3879
3907
  throw new CofheError({
@@ -3991,7 +4019,7 @@ function parseCompletedSealOutputResponse(params) {
3991
4019
  }
3992
4020
  return convertSealedData(sealed);
3993
4021
  }
3994
- async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, acp, overallStartTime, retry404TimeoutMs, timeoutMs, onPoll) {
4022
+ async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, acp, overallStartTime, retry404TimeoutMs, timeoutMs, onPoll, signal) {
3995
4023
  const body = {
3996
4024
  ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
3997
4025
  host_chain_id: chainId,
@@ -4000,6 +4028,7 @@ async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, acp
4000
4028
  let attemptIndex = 0;
4001
4029
  let last404ApiErrorMessage;
4002
4030
  for (; ; ) {
4031
+ throwIfAborted(signal, "sealOutput");
4003
4032
  let response;
4004
4033
  try {
4005
4034
  response = await cofheFetch(
@@ -4010,10 +4039,12 @@ async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, acp
4010
4039
  headers: {
4011
4040
  "Content-Type": "application/json"
4012
4041
  },
4013
- body: JSON.stringify(body)
4042
+ body: JSON.stringify(body),
4043
+ signal
4014
4044
  }
4015
4045
  );
4016
4046
  } catch (e) {
4047
+ throwIfAborted(signal, "sealOutput");
4017
4048
  throw new CofheError({
4018
4049
  code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
4019
4050
  message: `sealOutput request failed`,
@@ -4108,13 +4139,14 @@ async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, acp
4108
4139
  intervalMs: SUBMIT_RETRY_INTERVAL_MS,
4109
4140
  timeoutMs
4110
4141
  });
4111
- await new Promise((resolve) => setTimeout(resolve, SUBMIT_RETRY_INTERVAL_MS));
4142
+ await sleepUnlessAborted(SUBMIT_RETRY_INTERVAL_MS, signal, "sealOutput");
4112
4143
  attemptIndex += 1;
4113
4144
  }
4114
4145
  }
4115
- async function pollSealOutputStatus(thresholdNetworkUrl, requestId, overallStartTime, timeoutMs, onPoll) {
4146
+ async function pollSealOutputStatus(thresholdNetworkUrl, requestId, overallStartTime, timeoutMs, onPoll, signal) {
4116
4147
  let attemptIndex = 0;
4117
4148
  while (true) {
4149
+ throwIfAborted(signal, "sealOutput");
4118
4150
  const elapsedMs = Date.now() - overallStartTime;
4119
4151
  const intervalMs = computeMinuteRampPollIntervalMs(elapsedMs, {
4120
4152
  minIntervalMs: POLL_INTERVAL_MS,
@@ -4149,7 +4181,8 @@ async function pollSealOutputStatus(thresholdNetworkUrl, requestId, overallStart
4149
4181
  method: "GET",
4150
4182
  headers: {
4151
4183
  "Content-Type": "application/json"
4152
- }
4184
+ },
4185
+ signal
4153
4186
  }
4154
4187
  );
4155
4188
  } catch (e) {
@@ -4210,7 +4243,7 @@ async function pollSealOutputStatus(thresholdNetworkUrl, requestId, overallStart
4210
4243
  requestId
4211
4244
  });
4212
4245
  }
4213
- await new Promise((resolve) => setTimeout(resolve, intervalMs));
4246
+ await sleepUnlessAborted(intervalMs, signal, "sealOutput");
4214
4247
  attemptIndex += 1;
4215
4248
  }
4216
4249
  throw new CofheError({
@@ -4223,7 +4256,7 @@ async function pollSealOutputStatus(thresholdNetworkUrl, requestId, overallStart
4223
4256
  });
4224
4257
  }
4225
4258
  async function tnSealOutputV2(params) {
4226
- const { thresholdNetworkUrl, ctHash, chainId, acp, retry404TimeoutMs, onPoll } = params;
4259
+ const { thresholdNetworkUrl, ctHash, chainId, acp, retry404TimeoutMs, onPoll, signal } = params;
4227
4260
  const timeoutMs = params.timeoutMs ?? DEFAULT_DECRYPT_TIMEOUT_MS;
4228
4261
  const normalized404RetryTimeoutMs = normalize404RetryTimeoutMs({
4229
4262
  timeoutMs: retry404TimeoutMs,
@@ -4239,12 +4272,20 @@ async function tnSealOutputV2(params) {
4239
4272
  overallStartTime,
4240
4273
  normalized404RetryTimeoutMs,
4241
4274
  timeoutMs,
4242
- onPoll
4275
+ onPoll,
4276
+ signal
4243
4277
  );
4244
4278
  if (submitResult.kind === "completed") {
4245
4279
  return submitResult.sealed;
4246
4280
  }
4247
- return await pollSealOutputStatus(thresholdNetworkUrl, submitResult.requestId, overallStartTime, timeoutMs, onPoll);
4281
+ return await pollSealOutputStatus(
4282
+ thresholdNetworkUrl,
4283
+ submitResult.requestId,
4284
+ overallStartTime,
4285
+ timeoutMs,
4286
+ onPoll,
4287
+ signal
4288
+ );
4248
4289
  }
4249
4290
 
4250
4291
  // core/decrypt/decryptForViewBuilder.ts
@@ -4257,6 +4298,8 @@ var DecryptForViewBuilder = class extends BaseBuilder {
4257
4298
  pollCallback;
4258
4299
  retry404TimeoutMs = DEFAULT_404_RETRY_TIMEOUT_MS2;
4259
4300
  timeoutMs;
4301
+ signal;
4302
+ limit;
4260
4303
  constructor(params) {
4261
4304
  super({
4262
4305
  config: params.config,
@@ -4270,6 +4313,7 @@ var DecryptForViewBuilder = class extends BaseBuilder {
4270
4313
  this.utype = params.utype;
4271
4314
  this.acpHash = params.acpHash;
4272
4315
  this.acp = params.acp;
4316
+ this.limit = params.limit ?? ((task) => task());
4273
4317
  }
4274
4318
  /**
4275
4319
  * @param chainId - Chain to decrypt values from. Used to fetch the threshold network URL and use the correct acp.
@@ -4340,6 +4384,15 @@ var DecryptForViewBuilder = class extends BaseBuilder {
4340
4384
  this.timeoutMs = timeoutMs;
4341
4385
  return this;
4342
4386
  }
4387
+ /**
4388
+ * Stops the decrypt when `signal` fires: the pending request is aborted, no further attempt is
4389
+ * sent, and `execute()` rejects with `Aborted`. A request already processing on the threshold
4390
+ * network is not cancelled.
4391
+ */
4392
+ setSignal(signal) {
4393
+ this.signal = signal;
4394
+ return this;
4395
+ }
4343
4396
  withACP(acpOrACPHash) {
4344
4397
  if (typeof acpOrACPHash === "string") {
4345
4398
  this.acpHash = acpOrACPHash;
@@ -4463,15 +4516,20 @@ var DecryptForViewBuilder = class extends BaseBuilder {
4463
4516
  this.assertPublicClient();
4464
4517
  const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
4465
4518
  const wireAcp = ACPUtils.getPublic(acp, true);
4466
- const sealed = await tnSealOutputV2({
4467
- ctHash: this.ctHash,
4468
- chainId: this.chainId,
4469
- acp: wireAcp,
4470
- thresholdNetworkUrl,
4471
- retry404TimeoutMs: this.retry404TimeoutMs,
4472
- timeoutMs: this.timeoutMs,
4473
- onPoll: this.pollCallback
4474
- });
4519
+ const sealed = await this.limit(
4520
+ () => tnSealOutputV2({
4521
+ ctHash: this.ctHash,
4522
+ chainId: this.chainId,
4523
+ acp: wireAcp,
4524
+ thresholdNetworkUrl,
4525
+ retry404TimeoutMs: this.retry404TimeoutMs,
4526
+ timeoutMs: this.timeoutMs,
4527
+ onPoll: this.pollCallback,
4528
+ signal: this.signal
4529
+ }),
4530
+ "sealOutput",
4531
+ this.signal
4532
+ );
4475
4533
  return ACPUtils.unseal(acp, sealed);
4476
4534
  }
4477
4535
  /**
@@ -4688,7 +4746,7 @@ function assertDecryptStatusResponseV2(value) {
4688
4746
  }
4689
4747
  return value;
4690
4748
  }
4691
- async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, acp, overallStartTime, retry404TimeoutMs, timeoutMs, onPoll) {
4749
+ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, acp, overallStartTime, retry404TimeoutMs, timeoutMs, onPoll, signal) {
4692
4750
  const body = {
4693
4751
  ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
4694
4752
  host_chain_id: chainId
@@ -4699,6 +4757,7 @@ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, acp,
4699
4757
  let attemptIndex = 0;
4700
4758
  let last404ApiErrorMessage;
4701
4759
  for (; ; ) {
4760
+ throwIfAborted(signal, "decrypt");
4702
4761
  let response;
4703
4762
  try {
4704
4763
  response = await cofheFetch(
@@ -4709,10 +4768,12 @@ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, acp,
4709
4768
  headers: {
4710
4769
  "Content-Type": "application/json"
4711
4770
  },
4712
- body: JSON.stringify(body)
4771
+ body: JSON.stringify(body),
4772
+ signal
4713
4773
  }
4714
4774
  );
4715
4775
  } catch (e) {
4776
+ throwIfAborted(signal, "decrypt");
4716
4777
  throw new CofheError({
4717
4778
  code: "DECRYPT_FAILED" /* DecryptFailed */,
4718
4779
  message: `decrypt request failed`,
@@ -4809,13 +4870,14 @@ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, acp,
4809
4870
  intervalMs: SUBMIT_RETRY_INTERVAL_MS2,
4810
4871
  timeoutMs
4811
4872
  });
4812
- await new Promise((resolve) => setTimeout(resolve, SUBMIT_RETRY_INTERVAL_MS2));
4873
+ await sleepUnlessAborted(SUBMIT_RETRY_INTERVAL_MS2, signal, "decrypt");
4813
4874
  attemptIndex += 1;
4814
4875
  }
4815
4876
  }
4816
- async function pollDecryptStatusV2(thresholdNetworkUrl, requestId, overallStartTime, timeoutMs, onPoll) {
4877
+ async function pollDecryptStatusV2(thresholdNetworkUrl, requestId, overallStartTime, timeoutMs, onPoll, signal) {
4817
4878
  let attemptIndex = 0;
4818
4879
  while (true) {
4880
+ throwIfAborted(signal, "decrypt");
4819
4881
  const elapsedMs = Date.now() - overallStartTime;
4820
4882
  const intervalMs = computeMinuteRampPollIntervalMs(elapsedMs, {
4821
4883
  minIntervalMs: POLL_INTERVAL_MS2,
@@ -4850,7 +4912,8 @@ async function pollDecryptStatusV2(thresholdNetworkUrl, requestId, overallStartT
4850
4912
  method: "GET",
4851
4913
  headers: {
4852
4914
  "Content-Type": "application/json"
4853
- }
4915
+ },
4916
+ signal
4854
4917
  }
4855
4918
  );
4856
4919
  } catch (e) {
@@ -4912,7 +4975,7 @@ async function pollDecryptStatusV2(thresholdNetworkUrl, requestId, overallStartT
4912
4975
  requestId
4913
4976
  });
4914
4977
  }
4915
- await new Promise((resolve) => setTimeout(resolve, intervalMs));
4978
+ await sleepUnlessAborted(intervalMs, signal, "decrypt");
4916
4979
  attemptIndex += 1;
4917
4980
  }
4918
4981
  throw new CofheError({
@@ -4925,7 +4988,7 @@ async function pollDecryptStatusV2(thresholdNetworkUrl, requestId, overallStartT
4925
4988
  });
4926
4989
  }
4927
4990
  async function tnDecryptV2(params) {
4928
- const { thresholdNetworkUrl, ctHash, chainId, acp, retry404TimeoutMs, onPoll } = params;
4991
+ const { thresholdNetworkUrl, ctHash, chainId, acp, retry404TimeoutMs, onPoll, signal } = params;
4929
4992
  const timeoutMs = params.timeoutMs ?? DEFAULT_DECRYPT_TIMEOUT_MS;
4930
4993
  const normalized404RetryTimeoutMs = normalize404RetryTimeoutMs({
4931
4994
  timeoutMs: retry404TimeoutMs,
@@ -4941,12 +5004,20 @@ async function tnDecryptV2(params) {
4941
5004
  overallStartTime,
4942
5005
  normalized404RetryTimeoutMs,
4943
5006
  timeoutMs,
4944
- onPoll
5007
+ onPoll,
5008
+ signal
4945
5009
  );
4946
5010
  if (submitResult.kind === "completed") {
4947
5011
  return submitResult;
4948
5012
  }
4949
- return await pollDecryptStatusV2(thresholdNetworkUrl, submitResult.requestId, overallStartTime, timeoutMs, onPoll);
5013
+ return await pollDecryptStatusV2(
5014
+ thresholdNetworkUrl,
5015
+ submitResult.requestId,
5016
+ overallStartTime,
5017
+ timeoutMs,
5018
+ onPoll,
5019
+ signal
5020
+ );
4950
5021
  }
4951
5022
 
4952
5023
  // core/decrypt/decryptForTxBuilder.ts
@@ -4959,6 +5030,8 @@ var DecryptForTxBuilder = class extends BaseBuilder {
4959
5030
  pollCallback;
4960
5031
  retry404TimeoutMs = DEFAULT_404_RETRY_TIMEOUT_MS3;
4961
5032
  timeoutMs;
5033
+ signal;
5034
+ limit;
4962
5035
  constructor(params) {
4963
5036
  super({
4964
5037
  config: params.config,
@@ -4969,6 +5042,7 @@ var DecryptForTxBuilder = class extends BaseBuilder {
4969
5042
  requireConnected: params.requireConnected
4970
5043
  });
4971
5044
  this.ctHash = params.ctHash;
5045
+ this.limit = params.limit ?? ((task) => task());
4972
5046
  }
4973
5047
  setChainId(chainId) {
4974
5048
  this.chainId = chainId;
@@ -5006,6 +5080,10 @@ var DecryptForTxBuilder = class extends BaseBuilder {
5006
5080
  this.timeoutMs = timeoutMs;
5007
5081
  return this;
5008
5082
  }
5083
+ setSignal(signal) {
5084
+ this.signal = signal;
5085
+ return this;
5086
+ }
5009
5087
  withACP(acpOrACPHash) {
5010
5088
  if (this.acpSelection === "with-acp") {
5011
5089
  throw new CofheError({
@@ -5133,15 +5211,20 @@ var DecryptForTxBuilder = class extends BaseBuilder {
5133
5211
  this.assertPublicClient();
5134
5212
  const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
5135
5213
  const wireAcp = acp ? ACPUtils.getPublic(acp, true) : null;
5136
- const { decryptedValue, signature } = await tnDecryptV2({
5137
- ctHash: this.ctHash,
5138
- chainId: this.chainId,
5139
- acp: wireAcp,
5140
- thresholdNetworkUrl,
5141
- retry404TimeoutMs: this.retry404TimeoutMs,
5142
- timeoutMs: this.timeoutMs,
5143
- onPoll: this.pollCallback
5144
- });
5214
+ const { decryptedValue, signature } = await this.limit(
5215
+ () => tnDecryptV2({
5216
+ ctHash: this.ctHash,
5217
+ chainId: this.chainId,
5218
+ acp: wireAcp,
5219
+ thresholdNetworkUrl,
5220
+ retry404TimeoutMs: this.retry404TimeoutMs,
5221
+ timeoutMs: this.timeoutMs,
5222
+ onPoll: this.pollCallback,
5223
+ signal: this.signal
5224
+ }),
5225
+ "decrypt",
5226
+ this.signal
5227
+ );
5145
5228
  return {
5146
5229
  ctHash: this.ctHash,
5147
5230
  decryptedValue,
@@ -5179,6 +5262,39 @@ var DecryptForTxBuilder = class extends BaseBuilder {
5179
5262
  }
5180
5263
  }
5181
5264
  };
5265
+
5266
+ // core/decrypt/decryptLimiter.ts
5267
+ function createDecryptLimiter(max) {
5268
+ let active = 0;
5269
+ const waiting = [];
5270
+ return async (task, operationLabel, signal) => {
5271
+ if (signal?.aborted)
5272
+ throw decryptAbortedError(operationLabel, signal);
5273
+ if (active >= max) {
5274
+ await new Promise((resume, reject) => {
5275
+ const onAbort = () => {
5276
+ const index = waiting.indexOf(start);
5277
+ if (index >= 0)
5278
+ waiting.splice(index, 1);
5279
+ reject(decryptAbortedError(operationLabel, signal));
5280
+ };
5281
+ const start = () => {
5282
+ signal?.removeEventListener("abort", onAbort);
5283
+ resume();
5284
+ };
5285
+ signal?.addEventListener("abort", onAbort, { once: true });
5286
+ waiting.push(start);
5287
+ });
5288
+ }
5289
+ active += 1;
5290
+ try {
5291
+ return await task();
5292
+ } finally {
5293
+ active -= 1;
5294
+ waiting.shift()?.();
5295
+ }
5296
+ };
5297
+ }
5182
5298
  var decryptResultSignerAbi = viem.parseAbi(["function decryptResultSigner() view returns (address)"]);
5183
5299
  var UINT_TYPE_MASK2 = 0x7fn;
5184
5300
  var TYPE_BYTE_OFFSET2 = 8n;
@@ -5324,11 +5440,13 @@ function createCofheClientBase(opts) {
5324
5440
  requireConnected: _requireConnected
5325
5441
  });
5326
5442
  }
5443
+ const decryptLimiter = createDecryptLimiter(opts.config.decryptConcurrency);
5327
5444
  function decryptForView(ctHash, utype) {
5328
5445
  const state = connectStore.getState();
5329
5446
  return new DecryptForViewBuilder({
5330
5447
  ctHash,
5331
5448
  utype,
5449
+ limit: decryptLimiter,
5332
5450
  chainId: state.chainId,
5333
5451
  account: state.account,
5334
5452
  config: opts.config,
@@ -5341,6 +5459,7 @@ function createCofheClientBase(opts) {
5341
5459
  const state = connectStore.getState();
5342
5460
  return new DecryptForTxBuilder({
5343
5461
  ctHash,
5462
+ limit: decryptLimiter,
5344
5463
  chainId: state.chainId,
5345
5464
  account: state.account,
5346
5465
  config: opts.config,
package/dist/node.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as CofheInputConfig, a as CofheConfig, b as CofheClient } from './clientTypes-BCAMwrqk.cjs';
1
+ import { C as CofheInputConfig, a as CofheConfig, b as CofheClient } from './clientTypes-K6XNB9H2.cjs';
2
2
  import 'viem';
3
3
  import './types-C07FK-cL.cjs';
4
4
  import 'zod';
package/dist/node.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as CofheInputConfig, a as CofheConfig, b as CofheClient } from './clientTypes-LvslG7o0.js';
1
+ import { C as CofheInputConfig, a as CofheConfig, b as CofheClient } from './clientTypes-DFXuL9kz.js';
2
2
  import 'viem';
3
3
  import './types-C07FK-cL.js';
4
4
  import 'zod';
package/dist/node.js CHANGED
@@ -1,8 +1,8 @@
1
- import { createCofheConfigBase, createCofheClientBase } from './chunk-KHM6LYLU.js';
1
+ import { createCofheConfigBase, createCofheClientBase } from './chunk-ZYCIUX5C.js';
2
2
  import './chunk-UKQAJK4N.js';
3
- import './chunk-WAA2U7CT.js';
4
- import { fromHexString } from './chunk-3SD2UCRE.js';
5
- import { TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT } from './chunk-OC5TGQGG.js';
3
+ import './chunk-37SG44CR.js';
4
+ import { fromHexString } from './chunk-PLHQYVND.js';
5
+ import { TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT } from './chunk-NJTYZ3CW.js';
6
6
  import { promises } from 'fs';
7
7
  import { join } from 'path';
8
8
  import { init_panic_hook, ProvenCompactCiphertextList, CompactPkeCrs, TfheCompactPublicKey } from 'node-tfhe';