@did-btcr2/method 0.61.0 → 0.63.0

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/cjs/index.js CHANGED
@@ -1370,7 +1370,18 @@ var Identifier = class _Identifier {
1370
1370
  if (encoded !== encoded.toLowerCase()) {
1371
1371
  throw new import_common8.IdentifierError(`Invalid method-specific id (must be lowercase): ${identifier}`, import_common8.INVALID_DID, { identifier });
1372
1372
  }
1373
- const { prefix: hrp, bytes: dataBytes } = import_base.bech32m.decodeToBytes(encoded);
1373
+ let hrp;
1374
+ let dataBytes;
1375
+ try {
1376
+ ({ prefix: hrp, bytes: dataBytes } = import_base.bech32m.decodeToBytes(encoded));
1377
+ } catch (error) {
1378
+ const reason = error instanceof Error ? error.message : String(error);
1379
+ throw new import_common8.IdentifierError(
1380
+ `Invalid method-specific id (Bech32m decoding failed: ${reason}): ${identifier}`,
1381
+ import_common8.INVALID_DID,
1382
+ { identifier, reason }
1383
+ );
1384
+ }
1374
1385
  if (!["x", "k"].includes(hrp)) {
1375
1386
  throw new import_common8.IdentifierError(`Invalid hrp: ${hrp}`, import_common8.INVALID_DID, { identifier });
1376
1387
  }
@@ -1803,6 +1814,7 @@ var BeaconSignalDiscovery = class _BeaconSignalDiscovery {
1803
1814
  static async indexer(beaconServices, bitcoin) {
1804
1815
  const beaconServiceSignals = /* @__PURE__ */ new Map();
1805
1816
  const currentBlockCount = await bitcoin.rest.block.count();
1817
+ const mediantimes = /* @__PURE__ */ new Map();
1806
1818
  for (const beaconService of beaconServices) {
1807
1819
  beaconServiceSignals.set(beaconService, []);
1808
1820
  const beaconAddress = BeaconUtils.parseBitcoinAddress(beaconService.serviceEndpoint);
@@ -1827,19 +1839,49 @@ var BeaconSignalDiscovery = class _BeaconSignalDiscovery {
1827
1839
  continue;
1828
1840
  }
1829
1841
  const confirmations = currentBlockCount - status.block_height + 1;
1842
+ const mediantime = await _BeaconSignalDiscovery.mediantime(status.block_hash, bitcoin, mediantimes);
1830
1843
  beaconServiceSignals.get(beaconService)?.push({
1831
1844
  tx: beaconSignal,
1832
1845
  signalBytes: updateHash,
1833
1846
  blockMetadata: {
1834
1847
  confirmations,
1835
1848
  height: status.block_height,
1836
- time: status.block_time
1849
+ time: status.block_time,
1850
+ mediantime
1837
1851
  }
1838
1852
  });
1839
1853
  }
1840
1854
  }
1841
1855
  return beaconServiceSignals;
1842
1856
  }
1857
+ /**
1858
+ * Return the median time past of a block, from the per-run cache or from the
1859
+ * Esplora block record (`GET /block/:hash`). The specification compares
1860
+ * `versionTime` with the block `mediantime`, and the address listing does not
1861
+ * carry it. One block record serves every signal in that block.
1862
+ * @param {string} blockhash The hash of the block that contains a signal.
1863
+ * @param {BitcoinConnection} bitcoin Bitcoin network connection to use for REST calls.
1864
+ * @param {Map<string, number>} cache The median time past values fetched so far, keyed by block hash.
1865
+ * @returns {Promise<number>} The median time past of the block, in Unix seconds.
1866
+ * @throws {ResolveError} `INTERNAL_ERROR` if the backend returns no block record or no `mediantime` for the hash.
1867
+ */
1868
+ static async mediantime(blockhash, bitcoin, cache) {
1869
+ const cached = cache.get(blockhash);
1870
+ if (cached !== void 0) {
1871
+ return cached;
1872
+ }
1873
+ const block = await bitcoin.rest.block.get({ blockhash });
1874
+ const mediantime = block?.mediantime;
1875
+ if (typeof mediantime !== "number" || !Number.isFinite(mediantime)) {
1876
+ throw new import_common10.ResolveError(
1877
+ `Block ${blockhash} has no mediantime in the block record of the Bitcoin REST backend.`,
1878
+ import_common10.INTERNAL_ERROR,
1879
+ { blockhash, block }
1880
+ );
1881
+ }
1882
+ cache.set(blockhash, mediantime);
1883
+ return mediantime;
1884
+ }
1843
1885
  /**
1844
1886
  * Traverse the full blockchain from genesis to chain top looking for beacon signals.
1845
1887
  * @param {Array<BeaconService>} beaconServices Array of BeaconService objects to search for signals.
@@ -1909,6 +1951,7 @@ var BeaconSignalDiscovery = class _BeaconSignalDiscovery {
1909
1951
  blockMetadata: {
1910
1952
  height: block.height,
1911
1953
  time: block.time,
1954
+ mediantime: block.mediantime,
1912
1955
  confirmations: block.confirmations
1913
1956
  }
1914
1957
  });
@@ -2428,17 +2471,46 @@ function isSMTProof(value) {
2428
2471
  function validateMinConf(value) {
2429
2472
  if (value === void 0) return DEFAULT_MIN_CONF;
2430
2473
  if (typeof value === "number" && Number.isInteger(value) && value >= 1) return value;
2431
- const shown = typeof value === "string" ? JSON.stringify(value) : String(value);
2432
2474
  throw new import_common13.ResolveError(
2433
- `Invalid resolution option minConf: expected a positive integer (minimum 1), got ${shown}.`,
2475
+ `Invalid resolution option minConf: expected a positive integer (minimum 1), got ${shown(value)}.`,
2434
2476
  import_common13.INVALID_OPTIONS,
2435
2477
  { minConf: value }
2436
2478
  );
2437
2479
  }
2480
+ function shown(value) {
2481
+ return typeof value === "string" ? JSON.stringify(value) : String(value);
2482
+ }
2483
+ var ASCII_INTEGER = /^-?[0-9]+$/;
2484
+ function validateVersionId(value) {
2485
+ if (value === void 0) return void 0;
2486
+ if (typeof value === "string" && ASCII_INTEGER.test(value) && Number.isSafeInteger(Number(value))) {
2487
+ return Number(value);
2488
+ }
2489
+ throw new import_common13.ResolveError(
2490
+ `Invalid resolution option versionId: expected an ASCII string of an integer, got ${shown(value)}.`,
2491
+ import_common13.INVALID_OPTIONS,
2492
+ { versionId: value }
2493
+ );
2494
+ }
2495
+ var UTC_XSD_DATETIME = /^-?\d{4,}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/;
2496
+ function validateVersionTime(value) {
2497
+ if (value === void 0) return void 0;
2498
+ if (typeof value === "string" && UTC_XSD_DATETIME.test(value) && import_common13.DateUtils.isValidXsdDateTime(value)) {
2499
+ const ms = Date.parse(value);
2500
+ if (Number.isFinite(ms)) return ms;
2501
+ }
2502
+ throw new import_common13.ResolveError(
2503
+ `Invalid resolution option versionTime: expected an XML Datetime in UTC without a fraction (for example "2026-07-01T00:00:00Z"), got ${shown(value)}.`,
2504
+ import_common13.INVALID_OPTIONS,
2505
+ { versionTime: value }
2506
+ );
2507
+ }
2438
2508
  var Resolver = class _Resolver {
2439
2509
  // --- Immutable inputs ---
2440
2510
  #didComponents;
2511
+ /** The parsed `ResolutionOptions.versionId`, or `undefined` when the option is absent. */
2441
2512
  #versionId;
2513
+ /** The parsed `ResolutionOptions.versionTime` in milliseconds since the Unix epoch, or `undefined`. */
2442
2514
  #versionTime;
2443
2515
  /**
2444
2516
  * The specific phase the Resolver is current in.
@@ -2449,22 +2521,28 @@ var Resolver = class _Resolver {
2449
2521
  #providedGenesisDocument = null;
2450
2522
  #beaconServicesSignals = /* @__PURE__ */ new Map();
2451
2523
  #processedServices = /* @__PURE__ */ new Set();
2524
+ /** The beacon addresses the resolver requested signals for: `scanned_beacons` of the specification. */
2452
2525
  #requestCache = /* @__PURE__ */ new Set();
2526
+ /**
2527
+ * The tuples of the specification's `updates` list: a signed update and the metadata of
2528
+ * the block that announced it. BeaconProcess appends; ProcessUpdate sorts the list and
2529
+ * removes one tuple per step. A tuple that one pass does not reach waits for the next.
2530
+ */
2453
2531
  #unsortedUpdates = [];
2454
2532
  #resolvedResponse = null;
2455
2533
  /**
2456
- * Monotonic DID-document version counter and the update-hash history that backs
2457
- * duplicate confirmation, both carried across the entire resolution. The spec's
2458
- * read algorithm keeps a single version counter and a single update-hash history
2459
- * for the whole signal-processing loop, re-deriving beacons from the contemporary
2460
- * document on each pass. This sans-I/O resolver splits that one loop into discovery
2461
- * rounds, so the two must persist across rounds rather than restart each pass.
2462
- * Restarting them would reject a legitimate linear history whose later updates are
2463
- * announced on beacons that earlier updates added: round two would forget it had
2464
- * already reached version two, see version three, and raise a late-publishing error.
2534
+ * The state of the specification loop, carried across every pass: the version counter
2535
+ * (`current_version_id`), the update-hash history that backs duplicate confirmation
2536
+ * (`update_hash_history`), the confirmations of the block that contains the most
2537
+ * recently applied unique update (`block_confirmations`), and the header time of that
2538
+ * block as `updated`. A pass that finds a new beacon address returns to discovery, so
2539
+ * the state must not restart: a restart would reject a linear history whose later
2540
+ * updates are announced on beacons that earlier updates added.
2465
2541
  */
2466
2542
  #currentVersionId = 1;
2467
2543
  #updateHashHistory = [];
2544
+ #blockConfirmations = 0;
2545
+ #updated;
2468
2546
  /**
2469
2547
  * Opt-in upper bound on multi-round beacon-discovery passes. `Infinity` (the
2470
2548
  * default) leaves discovery unbounded; termination is already guaranteed by
@@ -2489,8 +2567,15 @@ var Resolver = class _Resolver {
2489
2567
  this.#didComponents = didComponents;
2490
2568
  this.#sidecarData = sidecarData;
2491
2569
  this.#currentDocument = currentDocument;
2492
- this.#versionId = options?.versionId;
2493
- this.#versionTime = options?.versionTime;
2570
+ if (options?.versionId !== void 0 && options?.versionTime !== void 0) {
2571
+ throw new import_common13.ResolveError(
2572
+ "Invalid resolution options: versionId and versionTime are mutually exclusive. Pass one of them.",
2573
+ import_common13.INVALID_OPTIONS,
2574
+ { versionId: options.versionId, versionTime: options.versionTime }
2575
+ );
2576
+ }
2577
+ this.#versionId = validateVersionId(options?.versionId);
2578
+ this.#versionTime = validateVersionTime(options?.versionTime);
2494
2579
  const rounds = options?.maxDiscoveryRounds;
2495
2580
  this.#maxDiscoveryRounds = typeof rounds === "number" && rounds > 0 ? rounds : Infinity;
2496
2581
  this.#minConf = validateMinConf(options?.minConf);
@@ -2530,14 +2615,14 @@ var Resolver = class _Resolver {
2530
2615
  * @param {DidComponents} didComponents BTCR2 DID components used to resolve the DID Document
2531
2616
  * @param {object} genesisDocument The genesis document for resolving the DID Document.
2532
2617
  * @returns {DidDocument} The resolved DID Document object
2533
- * @throws {ResolveError} InvalidDidDocument if not conformant to DID Core v1.1
2618
+ * @throws {ResolveError} `INVALID_DID` if the hash of the genesis document is not the genesis bytes of the identifier
2534
2619
  */
2535
2620
  static external(didComponents, genesisDocument) {
2536
2621
  const genesisDocumentHash = (0, import_common13.canonicalHashBytes)(genesisDocument);
2537
2622
  if (!(0, import_utils7.equalBytes)(didComponents.genesisBytes, genesisDocumentHash)) {
2538
2623
  throw new import_common13.ResolveError(
2539
2624
  `Initial document mismatch: genesisBytes !== genesisDocumentHash`,
2540
- import_common13.INVALID_DID_DOCUMENT,
2625
+ import_common13.INVALID_DID,
2541
2626
  {
2542
2627
  genesisBytes: (0, import_common13.encode)(didComponents.genesisBytes, "hex"),
2543
2628
  genesisDocumentHash: (0, import_common13.encode)(genesisDocumentHash, "hex")
@@ -2574,89 +2659,7 @@ var Resolver = class _Resolver {
2574
2659
  return { updateMap, casMap, smtMap };
2575
2660
  }
2576
2661
  /**
2577
- * Implements subsection {@link https://dcdpr.github.io/did-btcr2/operations/resolve.html#process-updates | 7.2.f Process updates Array}.
2578
- * @param {DidDocument} currentDocument The current DID Document to apply the updates to.
2579
- * @param {Array<[SignedBTCR2Update, BlockMetadata]>} unsortedUpdates The unsorted array of BTCR2 Signed Updates and their associated Block Metadata.
2580
- * @param {string} [versionTime] The optional version time to limit updates to.
2581
- * @param {string} [versionId] The optional version id to limit updates to.
2582
- * @param {{ currentVersionId: number; updateHashHistory: HashBytes[] }} [resolutionState]
2583
- * Version counter and update-hash history carried from earlier discovery rounds.
2584
- * Standalone callers omit it and start fresh at version 1 with an empty history.
2585
- * @returns {DidResolutionResponse} The updated DID Document, number of confirmations, and version id.
2586
- *
2587
- * Confirmation depth is not checked here. The BeaconProcess phase excludes a
2588
- * signal below `ResolutionOptions.minConf` before its update reaches this method,
2589
- * so every tuple here comes from a block at or above the threshold.
2590
- */
2591
- static updates(currentDocument, unsortedUpdates, versionTime, versionId, resolutionState = { currentVersionId: 1, updateHashHistory: [] }) {
2592
- let currentVersionId = resolutionState.currentVersionId;
2593
- const updateHashHistory = resolutionState.updateHashHistory;
2594
- const updates = unsortedUpdates.sort(
2595
- ([upd0, blk0], [upd1, blk1]) => upd0.targetVersionId - upd1.targetVersionId || blk0.height - blk1.height
2596
- );
2597
- const response = {
2598
- didDocument: currentDocument,
2599
- metadata: {
2600
- versionId: `${currentVersionId}`,
2601
- confirmations: 0,
2602
- updated: "",
2603
- deactivated: currentDocument.deactivated || false
2604
- }
2605
- };
2606
- for (const [update, block] of updates) {
2607
- const currentDocumentHash = (0, import_common13.canonicalHashBytes)(response.didDocument);
2608
- const blocktime = import_common13.DateUtils.blocktimeToTimestamp(block.time);
2609
- response.metadata.updated = import_common13.DateUtils.toISOStringNonFractional(blocktime);
2610
- response.metadata.confirmations = block.confirmations;
2611
- if (update.targetVersionId <= currentVersionId) {
2612
- this.confirmDuplicate(update, updateHashHistory);
2613
- continue;
2614
- }
2615
- if (versionTime) {
2616
- if (blocktime > import_common13.DateUtils.dateStringToTimestamp(versionTime)) {
2617
- return response;
2618
- }
2619
- }
2620
- if (update.targetVersionId === currentVersionId + 1) {
2621
- const sourceHashBytes = (0, import_common13.decode)(update.sourceHash, "base64urlnopad");
2622
- if (!(0, import_utils7.equalBytes)(sourceHashBytes, currentDocumentHash)) {
2623
- throw new import_common13.ResolveError(
2624
- `Hash mismatch: update.sourceHash !== currentDocumentHash`,
2625
- import_common13.INVALID_DID_UPDATE,
2626
- {
2627
- sourceHash: update.sourceHash,
2628
- currentDocumentHash: (0, import_common13.encode)(currentDocumentHash, "hex")
2629
- }
2630
- );
2631
- }
2632
- response.didDocument = this.applyUpdate(response.didDocument, update);
2633
- const unsignedUpdate = import_common13.JSONUtils.deleteKeys(update, ["proof"]);
2634
- updateHashHistory.push((0, import_common13.canonicalHashBytes)(unsignedUpdate));
2635
- } else {
2636
- throw new import_common13.ResolveError(
2637
- `Version Id Mismatch: targetVersionId cannot be > currentVersionId + 1`,
2638
- import_common13.LATE_PUBLISHING_ERROR,
2639
- {
2640
- targetVersionId: update.targetVersionId,
2641
- currentVersionId: currentVersionId + 1
2642
- }
2643
- );
2644
- }
2645
- currentVersionId++;
2646
- response.metadata.versionId = `${currentVersionId}`;
2647
- const versionIdNumber = Number(versionId);
2648
- if (!isNaN(versionIdNumber) && versionIdNumber <= currentVersionId) {
2649
- return response;
2650
- }
2651
- if (response.didDocument.deactivated) {
2652
- response.metadata.deactivated = response.didDocument.deactivated;
2653
- return response;
2654
- }
2655
- }
2656
- return response;
2657
- }
2658
- /**
2659
- * Implements subsection {@link https://dcdpr.github.io/did-btcr2/#confirm-duplicate-update | 7.2.f.1 Confirm Duplicate Update}.
2662
+ * Implements subsection {@link https://dcdpr.github.io/did-btcr2/operations/resolve.html#confirm-duplicate-update | Confirm Duplicate Update}.
2660
2663
  * This step confirms that an update with a lower-than-expected targetVersionId is a true duplicate.
2661
2664
  * @param {SignedBTCR2Update} update The BTCR2 Signed Update to confirm as a duplicate.
2662
2665
  * @param {HashBytes[]} updateHashHistory The accumulated hash history for comparison.
@@ -2695,14 +2698,26 @@ var Resolver = class _Resolver {
2695
2698
  }
2696
2699
  }
2697
2700
  /**
2698
- * Implements subsection {@link https://dcdpr.github.io/did-btcr2/operations/resolve.html#apply-update | 7.2.f.3 Apply Update}
2701
+ * Implements subsection {@link https://dcdpr.github.io/did-btcr2/operations/resolve.html#apply-update | Apply update}
2699
2702
  * and its step {@link https://dcdpr.github.io/did-btcr2/operations/resolve.html#check-update-proof | Check update.proof}.
2700
2703
  * @param {DidDocument} currentDocument The current DID Document to apply the update to.
2701
2704
  * @param {SignedBTCR2Update} update The BTCR2 Signed Update to apply.
2702
2705
  * @returns {DidDocument} The updated DID Document after applying the update.
2703
- * @throws {ResolveError} If the update is invalid or cannot be applied.
2706
+ * @throws {ResolveError} `INVALID_DID_UPDATE` if the update is invalid or cannot be applied.
2704
2707
  */
2705
2708
  static applyUpdate(currentDocument, update) {
2709
+ const currentDocumentHash = (0, import_common13.canonicalHashBytes)(currentDocument);
2710
+ const sourceHashBytes = (0, import_common13.decode)(update.sourceHash, "base64urlnopad");
2711
+ if (!(0, import_utils7.equalBytes)(sourceHashBytes, currentDocumentHash)) {
2712
+ throw new import_common13.ResolveError(
2713
+ `Hash mismatch: update.sourceHash !== currentDocumentHash`,
2714
+ import_common13.INVALID_DID_UPDATE,
2715
+ {
2716
+ sourceHash: update.sourceHash,
2717
+ currentDocumentHash: (0, import_common13.encode)(currentDocumentHash, "hex")
2718
+ }
2719
+ );
2720
+ }
2706
2721
  if (!isBtcr2UpdateContext(update["@context"])) {
2707
2722
  throw new import_common13.ResolveError(
2708
2723
  "Invalid update: @context is not the array the specification pins for a BTCR2 Update",
@@ -2763,13 +2778,13 @@ var Resolver = class _Resolver {
2763
2778
  }
2764
2779
  const updatedDocument = import_common13.JSONPatch.apply(currentDocument, update.patch);
2765
2780
  DidDocument.validate(updatedDocument);
2766
- const currentDocumentHash = (0, import_common13.canonicalHashBytes)(updatedDocument);
2781
+ const updatedDocumentHash = (0, import_common13.canonicalHashBytes)(updatedDocument);
2767
2782
  const updateTargetHash = (0, import_common13.decode)(update.targetHash);
2768
- if (!(0, import_utils7.equalBytes)(updateTargetHash, currentDocumentHash)) {
2783
+ if (!(0, import_utils7.equalBytes)(updateTargetHash, updatedDocumentHash)) {
2769
2784
  throw new import_common13.ResolveError(
2770
- `Invalid update: update.targetHash !== currentDocumentHash`,
2785
+ `Invalid update: update.targetHash !== updatedDocumentHash`,
2771
2786
  import_common13.INVALID_DID_UPDATE,
2772
- { updateTargetHash, currentDocumentHash }
2787
+ { updateTargetHash, updatedDocumentHash }
2773
2788
  );
2774
2789
  }
2775
2790
  return updatedDocument;
@@ -2843,59 +2858,99 @@ var Resolver = class _Resolver {
2843
2858
  if (allNeeds.length > 0) {
2844
2859
  return { status: "action-required", needs: allNeeds };
2845
2860
  }
2846
- this.#phase = "ApplyUpdates" /* ApplyUpdates */;
2861
+ this.#phase = "ProcessUpdate" /* ProcessUpdate */;
2847
2862
  continue;
2848
2863
  }
2849
- // Phase: ApplyUpdates
2850
- // Apply collected updates, then check for new beacon services (multi-round).
2851
- case "ApplyUpdates" /* ApplyUpdates */: {
2852
- if (this.#unsortedUpdates.length > 0) {
2853
- this.#resolvedResponse = _Resolver.updates(
2854
- this.#currentDocument,
2855
- this.#unsortedUpdates,
2856
- this.#versionTime,
2857
- this.#versionId,
2858
- { currentVersionId: this.#currentVersionId, updateHashHistory: this.#updateHashHistory }
2859
- );
2860
- this.#currentVersionId = Number(this.#resolvedResponse.metadata.versionId);
2861
- this.#currentDocument = this.#resolvedResponse.didDocument;
2862
- this.#unsortedUpdates = [];
2863
- const beaconServices = BeaconUtils.getBeaconServices(this.#currentDocument);
2864
- const hasNewServices = beaconServices.some((service) => {
2865
- const address = BeaconUtils.parseBitcoinAddress(service.serviceEndpoint);
2866
- return !this.#requestCache.has(address);
2867
- });
2868
- if (hasNewServices) {
2869
- if (++this.#discoveryRounds > this.#maxDiscoveryRounds) {
2870
- throw new import_common13.ResolveError(
2871
- `Exceeded the configured maximum of ${this.#maxDiscoveryRounds} beacon-discovery rounds. Raise or remove ResolutionOptions.maxDiscoveryRounds to resolve this DID.`,
2872
- import_common13.INTERNAL_ERROR,
2873
- { maxDiscoveryRounds: this.#maxDiscoveryRounds, discoveryRounds: this.#discoveryRounds }
2874
- );
2864
+ // Phase: ProcessUpdate
2865
+ // Spec "Process Next Update": one tuple per step. The phase repeats until
2866
+ // the document resolves, or until an applied update adds a beacon address
2867
+ // that the resolver did not scan (then the pass returns to BeaconDiscovery).
2868
+ case "ProcessUpdate" /* ProcessUpdate */: {
2869
+ const document = this.#currentDocument;
2870
+ if (this.#versionId !== void 0 && this.#currentVersionId === this.#versionId) {
2871
+ this.#phase = "Complete" /* Complete */;
2872
+ continue;
2873
+ }
2874
+ if (this.#unsortedUpdates.length === 0 || document.deactivated) {
2875
+ if (this.#versionId !== void 0) {
2876
+ throw new import_common13.ResolveError(
2877
+ `Version ${this.#versionId} of the DID does not exist: the history ` + (document.deactivated ? `ends with the deactivation at version ${this.#currentVersionId}.` : `ends at version ${this.#currentVersionId}.`),
2878
+ import_common13.NOT_FOUND,
2879
+ { versionId: this.#versionId, currentVersionId: this.#currentVersionId }
2880
+ );
2881
+ }
2882
+ this.#phase = "Complete" /* Complete */;
2883
+ continue;
2884
+ }
2885
+ this.#unsortedUpdates.sort(
2886
+ ([upd0, blk0], [upd1, blk1]) => upd0.targetVersionId - upd1.targetVersionId || blk0.height - blk1.height
2887
+ );
2888
+ const [update, block] = this.#unsortedUpdates.shift();
2889
+ if (update.targetVersionId <= this.#currentVersionId) {
2890
+ _Resolver.confirmDuplicate(update, this.#updateHashHistory);
2891
+ continue;
2892
+ }
2893
+ if (this.#versionTime !== void 0 && block.mediantime * 1e3 > this.#versionTime) {
2894
+ this.#phase = "Complete" /* Complete */;
2895
+ continue;
2896
+ }
2897
+ if (update.targetVersionId !== this.#currentVersionId + 1) {
2898
+ throw new import_common13.ResolveError(
2899
+ `Version Id Mismatch: targetVersionId cannot be > currentVersionId + 1`,
2900
+ import_common13.LATE_PUBLISHING_ERROR,
2901
+ {
2902
+ targetVersionId: update.targetVersionId,
2903
+ currentVersionId: this.#currentVersionId + 1
2875
2904
  }
2876
- this.#phase = "BeaconDiscovery" /* BeaconDiscovery */;
2877
- continue;
2905
+ );
2906
+ }
2907
+ this.#currentDocument = _Resolver.applyUpdate(document, update);
2908
+ const unsignedUpdate = import_common13.JSONUtils.deleteKeys(update, ["proof"]);
2909
+ this.#updateHashHistory.push((0, import_common13.canonicalHashBytes)(unsignedUpdate));
2910
+ this.#currentVersionId++;
2911
+ this.#blockConfirmations = block.confirmations;
2912
+ this.#updated = import_common13.DateUtils.toISOStringNonFractional(import_common13.DateUtils.blocktimeToTimestamp(block.time));
2913
+ if (this.#hasUnscannedBeacons()) {
2914
+ if (++this.#discoveryRounds > this.#maxDiscoveryRounds) {
2915
+ throw new import_common13.ResolveError(
2916
+ `Exceeded the configured maximum of ${this.#maxDiscoveryRounds} beacon-discovery rounds. Raise or remove ResolutionOptions.maxDiscoveryRounds to resolve this DID.`,
2917
+ import_common13.INTERNAL_ERROR,
2918
+ { maxDiscoveryRounds: this.#maxDiscoveryRounds, discoveryRounds: this.#discoveryRounds }
2919
+ );
2878
2920
  }
2921
+ this.#phase = "BeaconDiscovery" /* BeaconDiscovery */;
2879
2922
  }
2880
- this.#phase = "Complete" /* Complete */;
2881
2923
  continue;
2882
2924
  }
2883
2925
  // Phase: Complete
2926
+ // The document metadata of the specification: versionId is current_version_id,
2927
+ // confirmations is block_confirmations (0 when no update applied), deactivated
2928
+ // is the flag of the document. `updated` is present after the first apply.
2884
2929
  case "Complete" /* Complete */: {
2885
- return {
2886
- status: "resolved",
2887
- result: this.#resolvedResponse ?? {
2888
- didDocument: this.#currentDocument,
2889
- metadata: {
2890
- versionId: this.#versionId ?? "1",
2891
- deactivated: this.#currentDocument.deactivated || false
2892
- }
2930
+ this.#resolvedResponse ??= {
2931
+ didDocument: this.#currentDocument,
2932
+ metadata: {
2933
+ versionId: `${this.#currentVersionId}`,
2934
+ confirmations: this.#blockConfirmations,
2935
+ ...this.#updated !== void 0 ? { updated: this.#updated } : {},
2936
+ deactivated: this.#currentDocument.deactivated || false
2893
2937
  }
2894
2938
  };
2939
+ return { status: "resolved", result: this.#resolvedResponse };
2895
2940
  }
2896
2941
  }
2897
2942
  }
2898
2943
  }
2944
+ /**
2945
+ * True if the current document carries a beacon service whose address the resolver
2946
+ * did not request signals for. "Find Beacon Signals" scans such an address on the
2947
+ * next pass.
2948
+ */
2949
+ #hasUnscannedBeacons() {
2950
+ return BeaconUtils.getBeaconServices(this.#currentDocument).some(
2951
+ (service) => !this.#requestCache.has(BeaconUtils.parseBitcoinAddress(service.serviceEndpoint))
2952
+ );
2953
+ }
2899
2954
  /**
2900
2955
  * Return the signals of one beacon service that resolution may process: the
2901
2956
  * signals with at least `#minConf` confirmations. The specification removes a
@@ -2904,10 +2959,11 @@ var Resolver = class _Resolver {
2904
2959
  * integer confirmation count is excluded too: that is a mempool transaction
2905
2960
  * from a driver that did not skip it.
2906
2961
  *
2907
- * An eligible signal must carry a finite block height and block time. A
2908
- * signal that passes the count but lacks them is malformed. It fails fast
2909
- * here with a typed error, in the style of the {@link provide} guards, and
2910
- * not later with an invalid date inside {@link updates}.
2962
+ * An eligible signal must carry a finite block height, block time, and block
2963
+ * median time past. A signal that passes the count but lacks them is
2964
+ * malformed. It fails fast here with a typed error, in the style of the
2965
+ * {@link provide} guards, and not later with an invalid date or a false
2966
+ * `versionTime` comparison in the ProcessUpdate phase.
2911
2967
  * @param {Array<BeaconSignal>} signals The signals the caller provided for one service.
2912
2968
  * @returns {Array<BeaconSignal>} The signals at or above the threshold, in the given order.
2913
2969
  * @throws {ResolveError} `INVALID_DID_UPDATE` for an eligible signal with no valid block metadata.
@@ -2920,11 +2976,17 @@ var Resolver = class _Resolver {
2920
2976
  if (!Number.isInteger(confirmations) || confirmations < this.#minConf) {
2921
2977
  continue;
2922
2978
  }
2923
- if (!Number.isFinite(block?.height) || !Number.isFinite(block?.time)) {
2979
+ if (!Number.isFinite(block?.height) || !Number.isFinite(block?.time) || !Number.isFinite(block?.mediantime)) {
2924
2980
  throw new import_common13.ResolveError(
2925
- `Beacon signal ${signal.signalBytes} has ${confirmations} confirmations but no valid block height or block time.`,
2981
+ `Beacon signal ${signal.signalBytes} has ${confirmations} confirmations but no valid block height, block time, or block mediantime.`,
2926
2982
  import_common13.INVALID_DID_UPDATE,
2927
- { signalBytes: signal.signalBytes, confirmations, height: block?.height, time: block?.time }
2983
+ {
2984
+ signalBytes: signal.signalBytes,
2985
+ confirmations,
2986
+ height: block?.height,
2987
+ time: block?.time,
2988
+ mediantime: block?.mediantime
2989
+ }
2928
2990
  );
2929
2991
  }
2930
2992
  eligible.push(signal);
@@ -1,5 +1,5 @@
1
1
  import { GENESIS_TX_ID, TXIN_WITNESS_COINBASE } from '@did-btcr2/bitcoin';
2
- import { ResolveError } from '@did-btcr2/common';
2
+ import { INTERNAL_ERROR, ResolveError } from '@did-btcr2/common';
3
3
  import { BeaconUtils } from './utils.js';
4
4
  /**
5
5
  * The serialized form of a Beacon Signal output: `OP_RETURN` (`0x6a`), the 32-byte push
@@ -107,6 +107,9 @@ export class BeaconSignalDiscovery {
107
107
  const beaconServiceSignals = new Map();
108
108
  // Fetch the current block count once before the loop
109
109
  const currentBlockCount = await bitcoin.rest.block.count();
110
+ // The median time past of each block that holds a signal, keyed by block hash.
111
+ // One fetch per distinct block for the whole run.
112
+ const mediantimes = new Map();
110
113
  // Iterate over each beacon
111
114
  for (const beaconService of beaconServices) {
112
115
  beaconServiceSignals.set(beaconService, []);
@@ -156,6 +159,10 @@ export class BeaconSignalDiscovery {
156
159
  }
157
160
  // Use the pre-fetched block count instead of calling per-signal
158
161
  const confirmations = currentBlockCount - status.block_height + 1;
162
+ // The address listing carries the header time of the block, not its median
163
+ // time past. The resolver compares `versionTime` with the median time past,
164
+ // so read it from the block record.
165
+ const mediantime = await BeaconSignalDiscovery.mediantime(status.block_hash, bitcoin, mediantimes);
159
166
  // Push the beacon signal object to the signals array for the beacon service
160
167
  beaconServiceSignals.get(beaconService)?.push({
161
168
  tx: beaconSignal,
@@ -164,12 +171,37 @@ export class BeaconSignalDiscovery {
164
171
  confirmations,
165
172
  height: status.block_height,
166
173
  time: status.block_time,
174
+ mediantime,
167
175
  }
168
176
  });
169
177
  }
170
178
  }
171
179
  return beaconServiceSignals;
172
180
  }
181
+ /**
182
+ * Return the median time past of a block, from the per-run cache or from the
183
+ * Esplora block record (`GET /block/:hash`). The specification compares
184
+ * `versionTime` with the block `mediantime`, and the address listing does not
185
+ * carry it. One block record serves every signal in that block.
186
+ * @param {string} blockhash The hash of the block that contains a signal.
187
+ * @param {BitcoinConnection} bitcoin Bitcoin network connection to use for REST calls.
188
+ * @param {Map<string, number>} cache The median time past values fetched so far, keyed by block hash.
189
+ * @returns {Promise<number>} The median time past of the block, in Unix seconds.
190
+ * @throws {ResolveError} `INTERNAL_ERROR` if the backend returns no block record or no `mediantime` for the hash.
191
+ */
192
+ static async mediantime(blockhash, bitcoin, cache) {
193
+ const cached = cache.get(blockhash);
194
+ if (cached !== undefined) {
195
+ return cached;
196
+ }
197
+ const block = await bitcoin.rest.block.get({ blockhash });
198
+ const mediantime = block?.mediantime;
199
+ if (typeof mediantime !== 'number' || !Number.isFinite(mediantime)) {
200
+ throw new ResolveError(`Block ${blockhash} has no mediantime in the block record of the Bitcoin REST backend.`, INTERNAL_ERROR, { blockhash, block });
201
+ }
202
+ cache.set(blockhash, mediantime);
203
+ return mediantime;
204
+ }
173
205
  /**
174
206
  * Traverse the full blockchain from genesis to chain top looking for beacon signals.
175
207
  * @param {Array<BeaconService>} beaconServices Array of BeaconService objects to search for signals.
@@ -290,6 +322,7 @@ export class BeaconSignalDiscovery {
290
322
  blockMetadata: {
291
323
  height: block.height,
292
324
  time: block.time,
325
+ mediantime: block.mediantime,
293
326
  confirmations: block.confirmations
294
327
  }
295
328
  });
@@ -1 +1 @@
1
- {"version":3,"file":"signal-discovery.js","sourceRoot":"","sources":["../../../../src/core/beacon/signal-discovery.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"signal-discovery.js","sourceRoot":"","sources":["../../../../src/core/beacon/signal-discovery.ts"],"names":[],"mappings":"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"}
@@ -111,8 +111,18 @@ export class Identifier {
111
111
  if (encoded !== encoded.toLowerCase()) {
112
112
  throw new IdentifierError(`Invalid method-specific id (must be lowercase): ${identifier}`, INVALID_DID, { identifier });
113
113
  }
114
- // 7. Bech32m-decode the id into its hrp and dataBytes.
115
- const { prefix: hrp, bytes: dataBytes } = bech32m.decodeToBytes(encoded);
114
+ // 7. Bech32m-decode the id into its hrp and dataBytes. The decoder throws its own Error
115
+ // for a bad character, a bad length, a bad checksum, or bad padding. The specification
116
+ // maps every decoding failure to INVALID_DID.
117
+ let hrp;
118
+ let dataBytes;
119
+ try {
120
+ ({ prefix: hrp, bytes: dataBytes } = bech32m.decodeToBytes(encoded));
121
+ }
122
+ catch (error) {
123
+ const reason = error instanceof Error ? error.message : String(error);
124
+ throw new IdentifierError(`Invalid method-specific id (Bech32m decoding failed: ${reason}): ${identifier}`, INVALID_DID, { identifier, reason });
125
+ }
116
126
  // 8. The hrp MUST be "k" (KEY) or "x" (EXTERNAL).
117
127
  if (!['x', 'k'].includes(hrp)) {
118
128
  throw new IdentifierError(`Invalid hrp: ${hrp}`, INVALID_DID, { identifier });