@provablehq/sdk 0.11.2 → 0.11.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.
@@ -439,7 +439,15 @@ class IndexedDBKeyStore {
439
439
  * @returns {string} the encrypted authorization in RFC 4648 standard Base64.
440
440
  */
441
441
  function encryptAuthorization(publicKey, authorization) {
442
- return encryptMessage(publicKey, authorization.toBytesLe());
442
+ // Zeroize the intermediate plaintext bytes regardless of success or
443
+ // failure — same pattern as encryptRegistrationRequest.
444
+ const bytes = authorization.toBytesLe();
445
+ try {
446
+ return encryptMessage(publicKey, bytes);
447
+ }
448
+ finally {
449
+ zeroizeBytes(bytes);
450
+ }
443
451
  }
444
452
  /**
445
453
  * Encrypt a ProvingRequest with a cryptobox X25519 public key (libsodium-compatible wire format).
@@ -450,7 +458,62 @@ function encryptAuthorization(publicKey, authorization) {
450
458
  * @returns {string} the encrypted ProvingRequest in RFC 4648 standard Base64.
451
459
  */
452
460
  function encryptProvingRequest(publicKey, provingRequest) {
453
- return encryptMessage(publicKey, provingRequest.toBytesLe());
461
+ // Zeroize the intermediate plaintext bytes regardless of success or
462
+ // failure — same pattern as encryptRegistrationRequest.
463
+ const bytes = provingRequest.toBytesLe();
464
+ try {
465
+ return encryptMessage(publicKey, bytes);
466
+ }
467
+ finally {
468
+ zeroizeBytes(bytes);
469
+ }
470
+ }
471
+ /**
472
+ * Serialize a ProvingRequest for later encryption with
473
+ * `encryptSerializedProvingRequest`.
474
+ *
475
+ * Useful when the request may have to be encrypted more than once: the
476
+ * delegated proving service's one-time keys are single-use, so a client that
477
+ * needs to resend a request (e.g. after the service rejected a spent or
478
+ * unknown `key_id`) can keep the serialized form and re-encrypt it for a
479
+ * fresh key instead of rebuilding and re-signing the request.
480
+ *
481
+ * The returned buffer contains the plaintext request (including the signed
482
+ * authorization and its inputs) and is owned by the caller: keep it only as
483
+ * long as a resend may still be needed, then overwrite it with
484
+ * `zeroizeBytes(serialized)`. Zeroization in JavaScript is best-effort (see
485
+ * `zeroizeBytes`), and transient copies made inside the wasm boundary by
486
+ * `toBytesLe` are outside its reach.
487
+ *
488
+ * @param {ProvingRequest} provingRequest the ProvingRequest to serialize.
489
+ *
490
+ * @returns {Uint8Array} the serialized ProvingRequest bytes.
491
+ */
492
+ function serializeProvingRequest(provingRequest) {
493
+ return provingRequest.toBytesLe();
494
+ }
495
+ /**
496
+ * Encrypt an already-serialized ProvingRequest (as produced by
497
+ * `serializeProvingRequest`) with a cryptobox X25519 public key
498
+ * (libsodium-compatible wire format).
499
+ *
500
+ * Produces exactly the same ciphertext format as `encryptProvingRequest` —
501
+ * `encryptSerializedProvingRequest(pk, serializeProvingRequest(req))` and
502
+ * `encryptProvingRequest(pk, req)` are interchangeable from the proving
503
+ * service's point of view.
504
+ *
505
+ * The input buffer is not mutated and deliberately not zeroized here: the
506
+ * caller keeps ownership so the same bytes can be re-encrypted for another
507
+ * one-time key (retry). Call `zeroizeBytes(serializedProvingRequest)` once
508
+ * no resend can be needed anymore.
509
+ *
510
+ * @param {string} publicKey The cryptobox X25519 public key to encrypt with (encoded in RFC 4648 standard Base64).
511
+ * @param {Uint8Array} serializedProvingRequest the serialized ProvingRequest bytes.
512
+ *
513
+ * @returns {string} the encrypted ProvingRequest in RFC 4648 standard Base64.
514
+ */
515
+ function encryptSerializedProvingRequest(publicKey, serializedProvingRequest) {
516
+ return encryptMessage(publicKey, serializedProvingRequest);
454
517
  }
455
518
  /**
456
519
  * Encrypt a view key with a cryptobox X25519 public key (libsodium-compatible wire format).
@@ -461,7 +524,15 @@ function encryptProvingRequest(publicKey, provingRequest) {
461
524
  * @returns {string} the encrypted view key in RFC 4648 standard Base64.
462
525
  */
463
526
  function encryptViewKey(publicKey, viewKey) {
464
- return encryptMessage(publicKey, viewKey.toBytesLe());
527
+ // Zeroize the intermediate plaintext bytes regardless of success or
528
+ // failure — same pattern as encryptRegistrationRequest.
529
+ const bytes = viewKey.toBytesLe();
530
+ try {
531
+ return encryptMessage(publicKey, bytes);
532
+ }
533
+ finally {
534
+ zeroizeBytes(bytes);
535
+ }
465
536
  }
466
537
  /**
467
538
  * Encrypt a record scanner registration request.
@@ -1532,7 +1603,7 @@ class AleoNetworkClient {
1532
1603
  else {
1533
1604
  this.headers = {
1534
1605
  // This is replaced by the actual version by a Rollup plugin
1535
- "X-Aleo-SDK-Version": "0.11.2",
1606
+ "X-Aleo-SDK-Version": "0.11.4",
1536
1607
  "X-Aleo-environment": environment(),
1537
1608
  };
1538
1609
  }
@@ -1548,7 +1619,7 @@ class AleoNetworkClient {
1548
1619
  else {
1549
1620
  this.headers = {
1550
1621
  // This is replaced by the actual version by a Rollup plugin
1551
- "X-Aleo-SDK-Version": "0.11.2",
1622
+ "X-Aleo-SDK-Version": "0.11.4",
1552
1623
  "X-Aleo-environment": environment(),
1553
1624
  };
1554
1625
  }
@@ -2409,7 +2480,7 @@ class AleoNetworkClient {
2409
2480
  async getProgramAmendmentCount(programId) {
2410
2481
  try {
2411
2482
  this.ctx = { "X-ALEO-METHOD": "getProgramAmendmentCount" };
2412
- const raw = await this.fetchRaw("/programs/" + programId + "/amendment_count");
2483
+ const raw = await this.fetchRaw("/program/" + programId + "/amendment_count");
2413
2484
  return JSON.parse(raw);
2414
2485
  }
2415
2486
  catch (error) {
@@ -5837,83 +5908,84 @@ class ProgramManager {
5837
5908
  this._keyStore = keyStore;
5838
5909
  }
5839
5910
  /**
5840
- * Build a ProgramImportsBuilder from a program and its imports.
5841
- * Fetches imports from the network if not provided, resolves transitive
5842
- * dependencies, and optionally pre-loads cached keys from the KeyStore.
5843
- *
5844
- * @param loadKeys When true (default), loads cached proving/verifying keys
5845
- * from the KeyStore into the builder. Set to false for authorization and
5846
- * proving request paths where keys are not synthesized.
5911
+ * Collect static imports declared by the entry program together with any
5912
+ * caller-provided imports, then resolve missing transitive dependencies.
5913
+ * Caller-provided program sources take precedence over network sources.
5847
5914
  */
5848
- async buildProgramImports(program, imports, loadKeys, entryFunction) {
5849
- const builder = new mainnet_js.ProgramImports();
5915
+ async collectProgramImports(program, imports, tolerateNetworkErrors) {
5850
5916
  const programSource = typeof program === "string" ? program : program.toString();
5851
- const programObj = mainnet_js.Program.fromString(programSource);
5852
- const importNames = programObj.getImports();
5853
- if (importNames.length === 0 && (!imports || Object.keys(imports).length === 0)) {
5854
- return { builder, importEditions: new Map() };
5855
- }
5856
- let resolvedImports = {};
5857
- if (imports) {
5858
- resolvedImports = { ...imports };
5859
- }
5860
- if (importNames.length > 0 && !imports) {
5917
+ const providedImports = { ...(imports ?? {}) };
5918
+ let resolvedImports = { ...providedImports };
5919
+ if (ProgramManager.getImportNames(programSource).length > 0) {
5861
5920
  try {
5862
- resolvedImports = await this.networkClient.getProgramImports(programSource);
5921
+ const networkImports = await this.networkClient.getProgramImports(programSource, { ...resolvedImports });
5922
+ resolvedImports = { ...networkImports, ...providedImports };
5863
5923
  }
5864
5924
  catch (e) {
5925
+ if (!tolerateNetworkErrors)
5926
+ throw e;
5865
5927
  logger.warn(`Failed to resolve program imports from network: ${e}.`);
5866
5928
  }
5867
5929
  }
5868
- // Build a map of which functions each import actually calls,
5869
- // so we only load keys for functions in the call chain.
5870
- const calledFunctions = ProgramManager.callGraphToMap(programObj.getCallGraph(entryFunction));
5871
- // Phase 1: Collect all programs via BFS, discovering transitive imports.
5872
- // The initial getProgramImports call above already resolves the full
5873
- // transitive closure via recursive DFS. The BFS loop here only needs
5874
- // to handle user-provided imports whose transitive deps may not yet be
5875
- // known. For each unknown import we issue a single getProgram() call
5876
- // (not a full recursive getProgramImports) and fetch siblings in
5877
- // parallel to minimize round-trips.
5878
5930
  const collected = new Map();
5879
- const collectQueue = Object.entries(resolvedImports).map(([name, src]) => [name, typeof src === "string" ? src : src.toString()]);
5931
+ const collectQueue = Object.entries(resolvedImports).map(([name, source]) => [name, typeof source === "string" ? source : source.toString()]);
5880
5932
  while (collectQueue.length > 0) {
5881
5933
  const [name, source] = collectQueue.shift();
5882
5934
  if (collected.has(name))
5883
5935
  continue;
5884
5936
  collected.set(name, source);
5885
- // Discover transitive imports for collection (call-graph tracing
5886
- // happens in a separate pass after topological sorting).
5887
5937
  try {
5888
5938
  const subImports = ProgramManager.getImportNames(source);
5889
- if (subImports.length > 0) {
5890
- // Fetch only unknown transitive imports — the source for
5891
- // programs already in collected/resolvedImports is known, so
5892
- // we skip them. Fetches within a BFS level run in parallel.
5893
- const unknownImports = subImports.filter((id) => !collected.has(id) && !(id in resolvedImports));
5894
- if (unknownImports.length > 0) {
5895
- const fetched = await Promise.all(unknownImports.map(async (id) => {
5896
- try {
5897
- const src = await this.networkClient.getProgram(id);
5898
- return [id, src];
5899
- }
5900
- catch {
5901
- return null;
5902
- }
5903
- }));
5904
- for (const entry of fetched) {
5905
- if (entry && !collected.has(entry[0])) {
5906
- collectQueue.push(entry);
5907
- }
5939
+ const unknownImports = subImports.filter((id) => !collected.has(id) && !(id in resolvedImports));
5940
+ if (unknownImports.length > 0) {
5941
+ const fetched = await Promise.all(unknownImports.map(async (id) => {
5942
+ try {
5943
+ const importedSource = await this.networkClient.getProgram(id);
5944
+ return [id, importedSource];
5945
+ }
5946
+ catch (e) {
5947
+ if (!tolerateNetworkErrors)
5948
+ throw e;
5949
+ return null;
5950
+ }
5951
+ }));
5952
+ for (const entry of fetched) {
5953
+ if (entry && !collected.has(entry[0])) {
5954
+ collectQueue.push(entry);
5908
5955
  }
5909
5956
  }
5910
5957
  }
5911
5958
  }
5912
5959
  catch (e) {
5960
+ if (!tolerateNetworkErrors)
5961
+ throw e;
5913
5962
  logger.warn(`Failed to resolve transitive imports for ${name}: ${e}`);
5914
5963
  }
5915
5964
  }
5916
- // Phase 2: Add programs in topological order (leaves first).
5965
+ return collected;
5966
+ }
5967
+ /**
5968
+ * Build a ProgramImportsBuilder from a program and its imports.
5969
+ * Fetches missing imports from the network, resolves transitive
5970
+ * dependencies, and optionally pre-loads cached keys from the KeyStore.
5971
+ *
5972
+ * @param loadKeys When true (default), loads cached proving/verifying keys
5973
+ * from the KeyStore into the builder. Set to false for authorization and
5974
+ * proving request paths where keys are not synthesized.
5975
+ */
5976
+ async buildProgramImports(program, imports, loadKeys, entryFunction) {
5977
+ const builder = new mainnet_js.ProgramImports();
5978
+ const programSource = typeof program === "string" ? program : program.toString();
5979
+ const programObj = mainnet_js.Program.fromString(programSource);
5980
+ const importNames = programObj.getImports();
5981
+ if (importNames.length === 0 && (!imports || Object.keys(imports).length === 0)) {
5982
+ return { builder, importEditions: new Map() };
5983
+ }
5984
+ const collected = await this.collectProgramImports(programSource, imports, true);
5985
+ // Build a map of which functions each import actually calls,
5986
+ // so we only load keys for functions in the call chain.
5987
+ const calledFunctions = ProgramManager.callGraphToMap(programObj.getCallGraph(entryFunction));
5988
+ // Add programs in topological order (leaves first).
5917
5989
  // A simple DFS post-order ensures dependencies are added before
5918
5990
  // dependents, regardless of the order collected entries arrived.
5919
5991
  const sorted = [];
@@ -8386,7 +8458,7 @@ class ProgramManager {
8386
8458
  throw new Error("Authorization must be provided if estimating fee for Authorization.");
8387
8459
  }
8388
8460
  const programSource = program ? program.toString() : await this.networkClient.getProgram(programName, edition);
8389
- const programImports = imports ?? await this.networkClient.getProgramImports(programSource);
8461
+ const programImports = Object.fromEntries(await this.collectProgramImports(programSource, imports, false));
8390
8462
  return mainnet_js.ProgramManager.estimateFeeForAuthorization(authorization, programSource, programImports, edition);
8391
8463
  }
8392
8464
  /**
@@ -8421,7 +8493,7 @@ class ProgramManager {
8421
8493
  throw new Error("Function name must be specified when estimating fee.");
8422
8494
  }
8423
8495
  const programSource = program ? program.toString() : await this.networkClient.getProgram(programName, edition);
8424
- const programImports = imports ? imports : await this.networkClient.getProgramImports(programSource);
8496
+ const programImports = Object.fromEntries(await this.collectProgramImports(programSource, imports, false));
8425
8497
  return mainnet_js.ProgramManager.estimateExecutionFee(programSource, functionName, programImports, edition);
8426
8498
  }
8427
8499
  // Internal utility function for getting a credits.aleo record
@@ -8465,7 +8537,7 @@ class ProgramManager {
8465
8537
  * import { AleoKeyProvider, getOrInitConsensusVersionTestHeights, ProgramManager, NetworkRecordProvider } from "@provablehq/sdk/mainnet.js";
8466
8538
  *
8467
8539
  * // Initialize the development consensus heights in order to work with devnode.
8468
- * getOrInitConsensusVersionTestHeights("0,1,2,3,4,5,6,7,8,9,10,11,12,13,14");
8540
+ * getOrInitConsensusVersionTestHeights("0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16");
8469
8541
  *
8470
8542
  * // Create a new NetworkClient and RecordProvider.
8471
8543
  * const recordProvider = new NetworkRecordProvider(account, networkClient);
@@ -8592,7 +8664,7 @@ class ProgramManager {
8592
8664
  * import { ProgramManager, NetworkRecordProvider, getOrInitConsensusVersionTestHeights } from "@provablehq/sdk/mainnet.js";
8593
8665
  *
8594
8666
  * // Initialize the development consensus heights in order to work with a local devnode.
8595
- * getOrInitConsensusVersionTestHeights("0,1,2,3,4,5,6,7,8,9,10,11,12,13,14");
8667
+ * getOrInitConsensusVersionTestHeights("0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16");
8596
8668
  *
8597
8669
  * // Create a new NetworkClient, and RecordProvider
8598
8670
  * const recordProvider = new NetworkRecordProvider(account, networkClient);
@@ -9412,6 +9484,7 @@ exports.defaultTransport = defaultTransport;
9412
9484
  exports.encryptAuthorization = encryptAuthorization;
9413
9485
  exports.encryptProvingRequest = encryptProvingRequest;
9414
9486
  exports.encryptRegistrationRequest = encryptRegistrationRequest;
9487
+ exports.encryptSerializedProvingRequest = encryptSerializedProvingRequest;
9415
9488
  exports.encryptViewKey = encryptViewKey;
9416
9489
  exports.generateHookData = generateHookData;
9417
9490
  exports.getLogLevel = getLogLevel;
@@ -9425,6 +9498,7 @@ exports.isViewKeyStrategy = isViewKeyStrategy;
9425
9498
  exports.logAndThrow = logAndThrow;
9426
9499
  exports.programChecksum = programChecksum;
9427
9500
  exports.provingKeyLocator = provingKeyLocator;
9501
+ exports.serializeProvingRequest = serializeProvingRequest;
9428
9502
  exports.setLogLevel = setLogLevel;
9429
9503
  exports.sha256Hex = sha256Hex;
9430
9504
  exports.toAddress = toAddress;