@did-btcr2/method 0.62.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/README.md +5 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/browser.js +3 -3
- package/dist/browser.mjs +3 -3
- package/dist/cjs/index.js +199 -149
- package/dist/esm/core/beacon/signal-discovery.js +34 -1
- package/dist/esm/core/beacon/signal-discovery.js.map +1 -1
- package/dist/esm/core/resolver.js +205 -195
- package/dist/esm/core/resolver.js.map +1 -1
- package/dist/types/core/beacon/interfaces.d.ts +9 -1
- package/dist/types/core/beacon/interfaces.d.ts.map +1 -1
- package/dist/types/core/beacon/signal-discovery.d.ts +12 -0
- package/dist/types/core/beacon/signal-discovery.d.ts.map +1 -1
- package/dist/types/core/interfaces.d.ts +16 -5
- package/dist/types/core/interfaces.d.ts.map +1 -1
- package/dist/types/core/resolver.d.ts +3 -23
- package/dist/types/core/resolver.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/core/beacon/interfaces.ts +10 -1
- package/src/core/beacon/signal-discovery.ts +44 -1
- package/src/core/interfaces.ts +16 -5
- package/src/core/resolver.ts +248 -243
package/dist/cjs/index.js
CHANGED
|
@@ -1814,6 +1814,7 @@ var BeaconSignalDiscovery = class _BeaconSignalDiscovery {
|
|
|
1814
1814
|
static async indexer(beaconServices, bitcoin) {
|
|
1815
1815
|
const beaconServiceSignals = /* @__PURE__ */ new Map();
|
|
1816
1816
|
const currentBlockCount = await bitcoin.rest.block.count();
|
|
1817
|
+
const mediantimes = /* @__PURE__ */ new Map();
|
|
1817
1818
|
for (const beaconService of beaconServices) {
|
|
1818
1819
|
beaconServiceSignals.set(beaconService, []);
|
|
1819
1820
|
const beaconAddress = BeaconUtils.parseBitcoinAddress(beaconService.serviceEndpoint);
|
|
@@ -1838,19 +1839,49 @@ var BeaconSignalDiscovery = class _BeaconSignalDiscovery {
|
|
|
1838
1839
|
continue;
|
|
1839
1840
|
}
|
|
1840
1841
|
const confirmations = currentBlockCount - status.block_height + 1;
|
|
1842
|
+
const mediantime = await _BeaconSignalDiscovery.mediantime(status.block_hash, bitcoin, mediantimes);
|
|
1841
1843
|
beaconServiceSignals.get(beaconService)?.push({
|
|
1842
1844
|
tx: beaconSignal,
|
|
1843
1845
|
signalBytes: updateHash,
|
|
1844
1846
|
blockMetadata: {
|
|
1845
1847
|
confirmations,
|
|
1846
1848
|
height: status.block_height,
|
|
1847
|
-
time: status.block_time
|
|
1849
|
+
time: status.block_time,
|
|
1850
|
+
mediantime
|
|
1848
1851
|
}
|
|
1849
1852
|
});
|
|
1850
1853
|
}
|
|
1851
1854
|
}
|
|
1852
1855
|
return beaconServiceSignals;
|
|
1853
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
|
+
}
|
|
1854
1885
|
/**
|
|
1855
1886
|
* Traverse the full blockchain from genesis to chain top looking for beacon signals.
|
|
1856
1887
|
* @param {Array<BeaconService>} beaconServices Array of BeaconService objects to search for signals.
|
|
@@ -1920,6 +1951,7 @@ var BeaconSignalDiscovery = class _BeaconSignalDiscovery {
|
|
|
1920
1951
|
blockMetadata: {
|
|
1921
1952
|
height: block.height,
|
|
1922
1953
|
time: block.time,
|
|
1954
|
+
mediantime: block.mediantime,
|
|
1923
1955
|
confirmations: block.confirmations
|
|
1924
1956
|
}
|
|
1925
1957
|
});
|
|
@@ -2439,17 +2471,46 @@ function isSMTProof(value) {
|
|
|
2439
2471
|
function validateMinConf(value) {
|
|
2440
2472
|
if (value === void 0) return DEFAULT_MIN_CONF;
|
|
2441
2473
|
if (typeof value === "number" && Number.isInteger(value) && value >= 1) return value;
|
|
2442
|
-
const shown = typeof value === "string" ? JSON.stringify(value) : String(value);
|
|
2443
2474
|
throw new import_common13.ResolveError(
|
|
2444
|
-
`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)}.`,
|
|
2445
2476
|
import_common13.INVALID_OPTIONS,
|
|
2446
2477
|
{ minConf: value }
|
|
2447
2478
|
);
|
|
2448
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
|
+
}
|
|
2449
2508
|
var Resolver = class _Resolver {
|
|
2450
2509
|
// --- Immutable inputs ---
|
|
2451
2510
|
#didComponents;
|
|
2511
|
+
/** The parsed `ResolutionOptions.versionId`, or `undefined` when the option is absent. */
|
|
2452
2512
|
#versionId;
|
|
2513
|
+
/** The parsed `ResolutionOptions.versionTime` in milliseconds since the Unix epoch, or `undefined`. */
|
|
2453
2514
|
#versionTime;
|
|
2454
2515
|
/**
|
|
2455
2516
|
* The specific phase the Resolver is current in.
|
|
@@ -2460,22 +2521,28 @@ var Resolver = class _Resolver {
|
|
|
2460
2521
|
#providedGenesisDocument = null;
|
|
2461
2522
|
#beaconServicesSignals = /* @__PURE__ */ new Map();
|
|
2462
2523
|
#processedServices = /* @__PURE__ */ new Set();
|
|
2524
|
+
/** The beacon addresses the resolver requested signals for: `scanned_beacons` of the specification. */
|
|
2463
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
|
+
*/
|
|
2464
2531
|
#unsortedUpdates = [];
|
|
2465
2532
|
#resolvedResponse = null;
|
|
2466
2533
|
/**
|
|
2467
|
-
*
|
|
2468
|
-
*
|
|
2469
|
-
*
|
|
2470
|
-
*
|
|
2471
|
-
*
|
|
2472
|
-
*
|
|
2473
|
-
*
|
|
2474
|
-
* announced on beacons that earlier updates added: round two would forget it had
|
|
2475
|
-
* 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.
|
|
2476
2541
|
*/
|
|
2477
2542
|
#currentVersionId = 1;
|
|
2478
2543
|
#updateHashHistory = [];
|
|
2544
|
+
#blockConfirmations = 0;
|
|
2545
|
+
#updated;
|
|
2479
2546
|
/**
|
|
2480
2547
|
* Opt-in upper bound on multi-round beacon-discovery passes. `Infinity` (the
|
|
2481
2548
|
* default) leaves discovery unbounded; termination is already guaranteed by
|
|
@@ -2500,8 +2567,15 @@ var Resolver = class _Resolver {
|
|
|
2500
2567
|
this.#didComponents = didComponents;
|
|
2501
2568
|
this.#sidecarData = sidecarData;
|
|
2502
2569
|
this.#currentDocument = currentDocument;
|
|
2503
|
-
|
|
2504
|
-
|
|
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);
|
|
2505
2579
|
const rounds = options?.maxDiscoveryRounds;
|
|
2506
2580
|
this.#maxDiscoveryRounds = typeof rounds === "number" && rounds > 0 ? rounds : Infinity;
|
|
2507
2581
|
this.#minConf = validateMinConf(options?.minConf);
|
|
@@ -2585,88 +2659,7 @@ var Resolver = class _Resolver {
|
|
|
2585
2659
|
return { updateMap, casMap, smtMap };
|
|
2586
2660
|
}
|
|
2587
2661
|
/**
|
|
2588
|
-
* Implements subsection {@link https://dcdpr.github.io/did-btcr2/operations/resolve.html#
|
|
2589
|
-
* @param {DidDocument} currentDocument The current DID Document to apply the updates to.
|
|
2590
|
-
* @param {Array<[SignedBTCR2Update, BlockMetadata]>} unsortedUpdates The unsorted array of BTCR2 Signed Updates and their associated Block Metadata.
|
|
2591
|
-
* @param {string} [versionTime] The optional version time to limit updates to.
|
|
2592
|
-
* @param {string} [versionId] The optional version id to limit updates to.
|
|
2593
|
-
* @param {{ currentVersionId: number; updateHashHistory: HashBytes[] }} [resolutionState]
|
|
2594
|
-
* Version counter and update-hash history carried from earlier discovery rounds.
|
|
2595
|
-
* Standalone callers omit it and start fresh at version 1 with an empty history.
|
|
2596
|
-
* @returns {DidResolutionResponse} The updated DID Document, number of confirmations, and version id.
|
|
2597
|
-
*
|
|
2598
|
-
* Confirmation depth is not checked here. The BeaconProcess phase excludes a
|
|
2599
|
-
* signal below `ResolutionOptions.minConf` before its update reaches this method,
|
|
2600
|
-
* so every tuple here comes from a block at or above the threshold.
|
|
2601
|
-
*/
|
|
2602
|
-
static updates(currentDocument, unsortedUpdates, versionTime, versionId, resolutionState = { currentVersionId: 1, updateHashHistory: [] }) {
|
|
2603
|
-
let currentVersionId = resolutionState.currentVersionId;
|
|
2604
|
-
const updateHashHistory = resolutionState.updateHashHistory;
|
|
2605
|
-
const updates = unsortedUpdates.sort(
|
|
2606
|
-
([upd0, blk0], [upd1, blk1]) => upd0.targetVersionId - upd1.targetVersionId || blk0.height - blk1.height
|
|
2607
|
-
);
|
|
2608
|
-
const response = {
|
|
2609
|
-
didDocument: currentDocument,
|
|
2610
|
-
metadata: {
|
|
2611
|
-
versionId: `${currentVersionId}`,
|
|
2612
|
-
confirmations: 0,
|
|
2613
|
-
deactivated: currentDocument.deactivated || false
|
|
2614
|
-
}
|
|
2615
|
-
};
|
|
2616
|
-
for (const [update, block] of updates) {
|
|
2617
|
-
const currentDocumentHash = (0, import_common13.canonicalHashBytes)(response.didDocument);
|
|
2618
|
-
const blocktime = import_common13.DateUtils.blocktimeToTimestamp(block.time);
|
|
2619
|
-
response.metadata.updated = import_common13.DateUtils.toISOStringNonFractional(blocktime);
|
|
2620
|
-
response.metadata.confirmations = block.confirmations;
|
|
2621
|
-
if (update.targetVersionId <= currentVersionId) {
|
|
2622
|
-
this.confirmDuplicate(update, updateHashHistory);
|
|
2623
|
-
continue;
|
|
2624
|
-
}
|
|
2625
|
-
if (versionTime) {
|
|
2626
|
-
if (blocktime > import_common13.DateUtils.dateStringToTimestamp(versionTime)) {
|
|
2627
|
-
return response;
|
|
2628
|
-
}
|
|
2629
|
-
}
|
|
2630
|
-
if (update.targetVersionId === currentVersionId + 1) {
|
|
2631
|
-
const sourceHashBytes = (0, import_common13.decode)(update.sourceHash, "base64urlnopad");
|
|
2632
|
-
if (!(0, import_utils7.equalBytes)(sourceHashBytes, currentDocumentHash)) {
|
|
2633
|
-
throw new import_common13.ResolveError(
|
|
2634
|
-
`Hash mismatch: update.sourceHash !== currentDocumentHash`,
|
|
2635
|
-
import_common13.INVALID_DID_UPDATE,
|
|
2636
|
-
{
|
|
2637
|
-
sourceHash: update.sourceHash,
|
|
2638
|
-
currentDocumentHash: (0, import_common13.encode)(currentDocumentHash, "hex")
|
|
2639
|
-
}
|
|
2640
|
-
);
|
|
2641
|
-
}
|
|
2642
|
-
response.didDocument = this.applyUpdate(response.didDocument, update);
|
|
2643
|
-
const unsignedUpdate = import_common13.JSONUtils.deleteKeys(update, ["proof"]);
|
|
2644
|
-
updateHashHistory.push((0, import_common13.canonicalHashBytes)(unsignedUpdate));
|
|
2645
|
-
} else {
|
|
2646
|
-
throw new import_common13.ResolveError(
|
|
2647
|
-
`Version Id Mismatch: targetVersionId cannot be > currentVersionId + 1`,
|
|
2648
|
-
import_common13.LATE_PUBLISHING_ERROR,
|
|
2649
|
-
{
|
|
2650
|
-
targetVersionId: update.targetVersionId,
|
|
2651
|
-
currentVersionId: currentVersionId + 1
|
|
2652
|
-
}
|
|
2653
|
-
);
|
|
2654
|
-
}
|
|
2655
|
-
currentVersionId++;
|
|
2656
|
-
response.metadata.versionId = `${currentVersionId}`;
|
|
2657
|
-
const versionIdNumber = Number(versionId);
|
|
2658
|
-
if (!isNaN(versionIdNumber) && versionIdNumber <= currentVersionId) {
|
|
2659
|
-
return response;
|
|
2660
|
-
}
|
|
2661
|
-
if (response.didDocument.deactivated) {
|
|
2662
|
-
response.metadata.deactivated = response.didDocument.deactivated;
|
|
2663
|
-
return response;
|
|
2664
|
-
}
|
|
2665
|
-
}
|
|
2666
|
-
return response;
|
|
2667
|
-
}
|
|
2668
|
-
/**
|
|
2669
|
-
* 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}.
|
|
2670
2663
|
* This step confirms that an update with a lower-than-expected targetVersionId is a true duplicate.
|
|
2671
2664
|
* @param {SignedBTCR2Update} update The BTCR2 Signed Update to confirm as a duplicate.
|
|
2672
2665
|
* @param {HashBytes[]} updateHashHistory The accumulated hash history for comparison.
|
|
@@ -2705,14 +2698,26 @@ var Resolver = class _Resolver {
|
|
|
2705
2698
|
}
|
|
2706
2699
|
}
|
|
2707
2700
|
/**
|
|
2708
|
-
* Implements subsection {@link https://dcdpr.github.io/did-btcr2/operations/resolve.html#apply-update |
|
|
2701
|
+
* Implements subsection {@link https://dcdpr.github.io/did-btcr2/operations/resolve.html#apply-update | Apply update}
|
|
2709
2702
|
* and its step {@link https://dcdpr.github.io/did-btcr2/operations/resolve.html#check-update-proof | Check update.proof}.
|
|
2710
2703
|
* @param {DidDocument} currentDocument The current DID Document to apply the update to.
|
|
2711
2704
|
* @param {SignedBTCR2Update} update The BTCR2 Signed Update to apply.
|
|
2712
2705
|
* @returns {DidDocument} The updated DID Document after applying the update.
|
|
2713
|
-
* @throws {ResolveError}
|
|
2706
|
+
* @throws {ResolveError} `INVALID_DID_UPDATE` if the update is invalid or cannot be applied.
|
|
2714
2707
|
*/
|
|
2715
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
|
+
}
|
|
2716
2721
|
if (!isBtcr2UpdateContext(update["@context"])) {
|
|
2717
2722
|
throw new import_common13.ResolveError(
|
|
2718
2723
|
"Invalid update: @context is not the array the specification pins for a BTCR2 Update",
|
|
@@ -2773,13 +2778,13 @@ var Resolver = class _Resolver {
|
|
|
2773
2778
|
}
|
|
2774
2779
|
const updatedDocument = import_common13.JSONPatch.apply(currentDocument, update.patch);
|
|
2775
2780
|
DidDocument.validate(updatedDocument);
|
|
2776
|
-
const
|
|
2781
|
+
const updatedDocumentHash = (0, import_common13.canonicalHashBytes)(updatedDocument);
|
|
2777
2782
|
const updateTargetHash = (0, import_common13.decode)(update.targetHash);
|
|
2778
|
-
if (!(0, import_utils7.equalBytes)(updateTargetHash,
|
|
2783
|
+
if (!(0, import_utils7.equalBytes)(updateTargetHash, updatedDocumentHash)) {
|
|
2779
2784
|
throw new import_common13.ResolveError(
|
|
2780
|
-
`Invalid update: update.targetHash !==
|
|
2785
|
+
`Invalid update: update.targetHash !== updatedDocumentHash`,
|
|
2781
2786
|
import_common13.INVALID_DID_UPDATE,
|
|
2782
|
-
{ updateTargetHash,
|
|
2787
|
+
{ updateTargetHash, updatedDocumentHash }
|
|
2783
2788
|
);
|
|
2784
2789
|
}
|
|
2785
2790
|
return updatedDocument;
|
|
@@ -2853,61 +2858,99 @@ var Resolver = class _Resolver {
|
|
|
2853
2858
|
if (allNeeds.length > 0) {
|
|
2854
2859
|
return { status: "action-required", needs: allNeeds };
|
|
2855
2860
|
}
|
|
2856
|
-
this.#phase = "
|
|
2861
|
+
this.#phase = "ProcessUpdate" /* ProcessUpdate */;
|
|
2857
2862
|
continue;
|
|
2858
2863
|
}
|
|
2859
|
-
// Phase:
|
|
2860
|
-
//
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
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
|
|
2885
2904
|
}
|
|
2886
|
-
|
|
2887
|
-
|
|
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
|
+
);
|
|
2888
2920
|
}
|
|
2921
|
+
this.#phase = "BeaconDiscovery" /* BeaconDiscovery */;
|
|
2889
2922
|
}
|
|
2890
|
-
this.#phase = "Complete" /* Complete */;
|
|
2891
2923
|
continue;
|
|
2892
2924
|
}
|
|
2893
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.
|
|
2894
2929
|
case "Complete" /* Complete */: {
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
confirmations: 0,
|
|
2903
|
-
deactivated: this.#currentDocument.deactivated || false
|
|
2904
|
-
}
|
|
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
|
|
2905
2937
|
}
|
|
2906
2938
|
};
|
|
2939
|
+
return { status: "resolved", result: this.#resolvedResponse };
|
|
2907
2940
|
}
|
|
2908
2941
|
}
|
|
2909
2942
|
}
|
|
2910
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
|
+
}
|
|
2911
2954
|
/**
|
|
2912
2955
|
* Return the signals of one beacon service that resolution may process: the
|
|
2913
2956
|
* signals with at least `#minConf` confirmations. The specification removes a
|
|
@@ -2916,10 +2959,11 @@ var Resolver = class _Resolver {
|
|
|
2916
2959
|
* integer confirmation count is excluded too: that is a mempool transaction
|
|
2917
2960
|
* from a driver that did not skip it.
|
|
2918
2961
|
*
|
|
2919
|
-
* An eligible signal must carry a finite block height
|
|
2920
|
-
* signal that passes the count but lacks them is
|
|
2921
|
-
* here with a typed error, in the style of the
|
|
2922
|
-
* not later with an invalid date
|
|
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.
|
|
2923
2967
|
* @param {Array<BeaconSignal>} signals The signals the caller provided for one service.
|
|
2924
2968
|
* @returns {Array<BeaconSignal>} The signals at or above the threshold, in the given order.
|
|
2925
2969
|
* @throws {ResolveError} `INVALID_DID_UPDATE` for an eligible signal with no valid block metadata.
|
|
@@ -2932,11 +2976,17 @@ var Resolver = class _Resolver {
|
|
|
2932
2976
|
if (!Number.isInteger(confirmations) || confirmations < this.#minConf) {
|
|
2933
2977
|
continue;
|
|
2934
2978
|
}
|
|
2935
|
-
if (!Number.isFinite(block?.height) || !Number.isFinite(block?.time)) {
|
|
2979
|
+
if (!Number.isFinite(block?.height) || !Number.isFinite(block?.time) || !Number.isFinite(block?.mediantime)) {
|
|
2936
2980
|
throw new import_common13.ResolveError(
|
|
2937
|
-
`Beacon signal ${signal.signalBytes} has ${confirmations} confirmations but no valid block height or block
|
|
2981
|
+
`Beacon signal ${signal.signalBytes} has ${confirmations} confirmations but no valid block height, block time, or block mediantime.`,
|
|
2938
2982
|
import_common13.INVALID_DID_UPDATE,
|
|
2939
|
-
{
|
|
2983
|
+
{
|
|
2984
|
+
signalBytes: signal.signalBytes,
|
|
2985
|
+
confirmations,
|
|
2986
|
+
height: block?.height,
|
|
2987
|
+
time: block?.time,
|
|
2988
|
+
mediantime: block?.mediantime
|
|
2989
|
+
}
|
|
2940
2990
|
);
|
|
2941
2991
|
}
|
|
2942
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":"AAKA,OAAO,EACL,aAAa,EACb,qBAAqB,EACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"signal-discovery.js","sourceRoot":"","sources":["../../../../src/core/beacon/signal-discovery.ts"],"names":[],"mappings":"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"}
|