@did-btcr2/method 0.20.0 → 0.23.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 +223 -1
- package/dist/browser.js +2566 -3507
- package/dist/browser.mjs +2566 -3507
- package/dist/cjs/core/beacon/aggregation/cohort/index.js +13 -2
- package/dist/cjs/core/beacon/aggregation/cohort/index.js.map +1 -1
- package/dist/cjs/core/beacon/aggregation/cohort/messages/base.js +7 -2
- package/dist/cjs/core/beacon/aggregation/cohort/messages/base.js.map +1 -1
- package/dist/cjs/core/beacon/aggregation/communication/adapter/did-comm.js +5 -10
- package/dist/cjs/core/beacon/aggregation/communication/adapter/did-comm.js.map +1 -1
- package/dist/cjs/core/beacon/aggregation/communication/adapter/nostr.js +4 -7
- package/dist/cjs/core/beacon/aggregation/communication/adapter/nostr.js.map +1 -1
- package/dist/cjs/core/beacon/aggregation/session/index.js +15 -3
- package/dist/cjs/core/beacon/aggregation/session/index.js.map +1 -1
- package/dist/cjs/core/beacon/cas-beacon.js +1 -1
- package/dist/cjs/core/beacon/cas-beacon.js.map +1 -1
- package/dist/cjs/core/beacon/signal-discovery.js +6 -6
- package/dist/cjs/core/beacon/signal-discovery.js.map +1 -1
- package/dist/cjs/core/beacon/singleton.js +18 -20
- package/dist/cjs/core/beacon/singleton.js.map +1 -1
- package/dist/cjs/core/beacon/smt-beacon.js +1 -1
- package/dist/cjs/core/beacon/smt-beacon.js.map +1 -1
- package/dist/cjs/core/identifier.js +11 -13
- package/dist/cjs/core/identifier.js.map +1 -1
- package/dist/cjs/core/resolve.js +52 -49
- package/dist/cjs/core/resolve.js.map +1 -1
- package/dist/cjs/core/update.js +3 -4
- package/dist/cjs/core/update.js.map +1 -1
- package/dist/cjs/did-btcr2.js +36 -35
- package/dist/cjs/did-btcr2.js.map +1 -1
- package/dist/cjs/utils/did-document-builder.js +0 -6
- package/dist/cjs/utils/did-document-builder.js.map +1 -1
- package/dist/cjs/utils/did-document.js +44 -26
- package/dist/cjs/utils/did-document.js.map +1 -1
- package/dist/esm/core/beacon/aggregation/cohort/index.js +13 -2
- package/dist/esm/core/beacon/aggregation/cohort/index.js.map +1 -1
- package/dist/esm/core/beacon/aggregation/cohort/messages/base.js +7 -2
- package/dist/esm/core/beacon/aggregation/cohort/messages/base.js.map +1 -1
- package/dist/esm/core/beacon/aggregation/communication/adapter/did-comm.js +5 -10
- package/dist/esm/core/beacon/aggregation/communication/adapter/did-comm.js.map +1 -1
- package/dist/esm/core/beacon/aggregation/communication/adapter/nostr.js +4 -7
- package/dist/esm/core/beacon/aggregation/communication/adapter/nostr.js.map +1 -1
- package/dist/esm/core/beacon/aggregation/session/index.js +15 -3
- package/dist/esm/core/beacon/aggregation/session/index.js.map +1 -1
- package/dist/esm/core/beacon/cas-beacon.js +1 -1
- package/dist/esm/core/beacon/cas-beacon.js.map +1 -1
- package/dist/esm/core/beacon/signal-discovery.js +6 -6
- package/dist/esm/core/beacon/signal-discovery.js.map +1 -1
- package/dist/esm/core/beacon/singleton.js +18 -20
- package/dist/esm/core/beacon/singleton.js.map +1 -1
- package/dist/esm/core/beacon/smt-beacon.js +1 -1
- package/dist/esm/core/beacon/smt-beacon.js.map +1 -1
- package/dist/esm/core/identifier.js +11 -13
- package/dist/esm/core/identifier.js.map +1 -1
- package/dist/esm/core/resolve.js +52 -49
- package/dist/esm/core/resolve.js.map +1 -1
- package/dist/esm/core/update.js +3 -4
- package/dist/esm/core/update.js.map +1 -1
- package/dist/esm/did-btcr2.js +36 -35
- package/dist/esm/did-btcr2.js.map +1 -1
- package/dist/esm/utils/did-document-builder.js +0 -6
- package/dist/esm/utils/did-document-builder.js.map +1 -1
- package/dist/esm/utils/did-document.js +44 -26
- package/dist/esm/utils/did-document.js.map +1 -1
- package/dist/types/core/beacon/aggregation/cohort/index.d.ts +1 -1
- package/dist/types/core/beacon/aggregation/cohort/index.d.ts.map +1 -1
- package/dist/types/core/beacon/aggregation/cohort/messages/base.d.ts +1 -1
- package/dist/types/core/beacon/aggregation/cohort/messages/base.d.ts.map +1 -1
- package/dist/types/core/beacon/aggregation/communication/adapter/did-comm.d.ts.map +1 -1
- package/dist/types/core/beacon/aggregation/communication/adapter/nostr.d.ts.map +1 -1
- package/dist/types/core/beacon/aggregation/session/index.d.ts +1 -1
- package/dist/types/core/beacon/aggregation/session/index.d.ts.map +1 -1
- package/dist/types/core/beacon/beacon.d.ts +3 -3
- package/dist/types/core/beacon/beacon.d.ts.map +1 -1
- package/dist/types/core/beacon/cas-beacon.d.ts +3 -3
- package/dist/types/core/beacon/cas-beacon.d.ts.map +1 -1
- package/dist/types/core/beacon/signal-discovery.d.ts +5 -5
- package/dist/types/core/beacon/signal-discovery.d.ts.map +1 -1
- package/dist/types/core/beacon/singleton.d.ts +4 -4
- package/dist/types/core/beacon/singleton.d.ts.map +1 -1
- package/dist/types/core/beacon/smt-beacon.d.ts +3 -3
- package/dist/types/core/beacon/smt-beacon.d.ts.map +1 -1
- package/dist/types/core/identifier.d.ts +17 -21
- package/dist/types/core/identifier.d.ts.map +1 -1
- package/dist/types/core/interfaces.d.ts +2 -2
- package/dist/types/core/interfaces.d.ts.map +1 -1
- package/dist/types/core/resolve.d.ts +10 -7
- package/dist/types/core/resolve.d.ts.map +1 -1
- package/dist/types/core/update.d.ts +2 -2
- package/dist/types/core/update.d.ts.map +1 -1
- package/dist/types/did-btcr2.d.ts +12 -8
- package/dist/types/did-btcr2.d.ts.map +1 -1
- package/dist/types/utils/did-document-builder.d.ts +0 -1
- package/dist/types/utils/did-document-builder.d.ts.map +1 -1
- package/dist/types/utils/did-document.d.ts +14 -9
- package/dist/types/utils/did-document.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/core/beacon/aggregation/cohort/index.ts +13 -2
- package/src/core/beacon/aggregation/cohort/messages/base.ts +7 -2
- package/src/core/beacon/aggregation/communication/adapter/did-comm.ts +5 -12
- package/src/core/beacon/aggregation/communication/adapter/nostr.ts +5 -8
- package/src/core/beacon/aggregation/session/index.ts +15 -3
- package/src/core/beacon/beacon.ts +3 -3
- package/src/core/beacon/cas-beacon.ts +3 -3
- package/src/core/beacon/signal-discovery.ts +9 -9
- package/src/core/beacon/singleton.ts +21 -23
- package/src/core/beacon/smt-beacon.ts +3 -3
- package/src/core/identifier.ts +31 -28
- package/src/core/interfaces.ts +2 -2
- package/src/core/resolve.ts +73 -62
- package/src/core/update.ts +5 -5
- package/src/did-btcr2.ts +47 -43
- package/src/utils/did-document-builder.ts +0 -7
- package/src/utils/did-document.ts +54 -31
package/src/core/resolve.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
BitcoinConnection,
|
|
3
3
|
getNetwork
|
|
4
4
|
} from '@did-btcr2/bitcoin';
|
|
5
5
|
import {
|
|
6
|
+
Canonicalization,
|
|
6
7
|
DateUtils,
|
|
7
8
|
IdentifierHrp,
|
|
8
9
|
INVALID_DID,
|
|
@@ -23,7 +24,7 @@ import {
|
|
|
23
24
|
} from '@did-btcr2/cryptosuite';
|
|
24
25
|
import { CompressedSecp256k1PublicKey } from '@did-btcr2/keypair';
|
|
25
26
|
import { bytesToHex } from '@noble/hashes/utils';
|
|
26
|
-
import {
|
|
27
|
+
import { DidBtcr2 } from '../did-btcr2.js';
|
|
27
28
|
import { Appendix } from '../utils/appendix.js';
|
|
28
29
|
import { DidDocument, ID_PLACEHOLDER_VALUE } from '../utils/did-document.js';
|
|
29
30
|
import { BeaconFactory } from './beacon/factory.js';
|
|
@@ -38,10 +39,13 @@ import { CASAnnouncement, Sidecar, SidecarData } from './types.js';
|
|
|
38
39
|
* The response object for DID Resolution.
|
|
39
40
|
*/
|
|
40
41
|
export interface DidResolutionResponse {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
didDocument: DidDocument;
|
|
43
|
+
metadata: {
|
|
44
|
+
confirmations?: number;
|
|
45
|
+
versionId: string;
|
|
46
|
+
updated?: string;
|
|
47
|
+
deactivated?: boolean;
|
|
48
|
+
}
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
/**
|
|
@@ -60,12 +64,12 @@ export class Resolve {
|
|
|
60
64
|
* @returns {DidDocument} The resolved DID Document object.
|
|
61
65
|
*/
|
|
62
66
|
static deterministic(didComponents: DidComponents): DidDocument {
|
|
63
|
-
// Encode the did from the didComponents
|
|
64
|
-
const did = Identifier.encode(didComponents);
|
|
65
|
-
|
|
66
67
|
// Deconstruct the bytes from the given components
|
|
67
68
|
const { genesisBytes } = didComponents;
|
|
68
69
|
|
|
70
|
+
// Encode the did from the didComponents
|
|
71
|
+
const did = Identifier.encode(genesisBytes, didComponents);
|
|
72
|
+
|
|
69
73
|
// Construct a new CompressedSecp256k1PublicKey and deconstruct the publicKey and publicKeyMultibase
|
|
70
74
|
const { multibase: publicKeyMultibase } = new CompressedSecp256k1PublicKey(genesisBytes);
|
|
71
75
|
|
|
@@ -79,7 +83,6 @@ export class Resolve {
|
|
|
79
83
|
|
|
80
84
|
return new DidDocument({
|
|
81
85
|
id : did,
|
|
82
|
-
controller : [did],
|
|
83
86
|
verificationMethod : [{
|
|
84
87
|
id : `${did}#initialKey`,
|
|
85
88
|
type : 'Multikey',
|
|
@@ -102,21 +105,20 @@ export class Resolve {
|
|
|
102
105
|
genesisDocument: object,
|
|
103
106
|
): Promise<DidDocument> {
|
|
104
107
|
// Canonicalize and sha256 hash the currentDocument
|
|
105
|
-
const hashBytes =
|
|
108
|
+
const hashBytes = Canonicalization.process(genesisDocument, { encoding: 'hex' });
|
|
106
109
|
|
|
107
|
-
|
|
108
|
-
const genesisBytes = bytesToHex(didComponents.genesisBytes);
|
|
110
|
+
const { genesisBytes } = didComponents;
|
|
109
111
|
|
|
110
112
|
// If the genesisBytes do not match the hashBytes, throw an error
|
|
111
|
-
if (genesisBytes !== hashBytes) {
|
|
113
|
+
if (bytesToHex(genesisBytes) !== hashBytes) {
|
|
112
114
|
throw new ResolveError(
|
|
113
|
-
`Initial document mismatch: genesisBytes ${genesisBytes} !== hashBytes ${hashBytes}`,
|
|
115
|
+
`Initial document mismatch: genesisBytes ${bytesToHex(genesisBytes)} !== hashBytes ${hashBytes}`,
|
|
114
116
|
INVALID_DID_DOCUMENT, { genesisBytes, hashBytes }
|
|
115
117
|
);
|
|
116
118
|
}
|
|
117
119
|
|
|
118
120
|
// Encode the did from the didComponents
|
|
119
|
-
const did = Identifier.encode(didComponents);
|
|
121
|
+
const did = Identifier.encode(genesisBytes, didComponents);
|
|
120
122
|
|
|
121
123
|
// Replace the placeholder did with the did throughout the currentDocument.
|
|
122
124
|
const currentDocument = JSON.parse(
|
|
@@ -137,14 +139,14 @@ export class Resolve {
|
|
|
137
139
|
const updateMap = new Map<string, SignedBTCR2Update>();
|
|
138
140
|
if(sidecar.updates?.length)
|
|
139
141
|
for(const update of sidecar.updates) {
|
|
140
|
-
updateMap.set(
|
|
142
|
+
updateMap.set(Canonicalization.process(update, { encoding: 'hex' }), update);
|
|
141
143
|
}
|
|
142
144
|
|
|
143
145
|
// CAS Announcements map
|
|
144
146
|
const casMap = new Map<string, CASAnnouncement>();
|
|
145
147
|
if(sidecar.casUpdates?.length)
|
|
146
148
|
for(const update of sidecar.casUpdates) {
|
|
147
|
-
casMap.set(
|
|
149
|
+
casMap.set(Canonicalization.process(update, { encoding: 'hex' }), update);
|
|
148
150
|
}
|
|
149
151
|
|
|
150
152
|
// SMT Proofs map
|
|
@@ -206,30 +208,32 @@ export class Resolve {
|
|
|
206
208
|
* Signal Bytes (last output in OP_RETURN transaction).
|
|
207
209
|
* @param {Array<BeaconService>} beaconServices The array of BeaconService objects to search for signals.
|
|
208
210
|
* @param {SidecarData} sidecarData The sidecar data containing maps of updates, CAS announcements, and SMT proofs.
|
|
209
|
-
* @param {
|
|
211
|
+
* @param {BitcoinConnection} bitcoin The bitcoin network connection used to fetch beacon signals
|
|
210
212
|
* @returns {Promise<Array<[SignedBTCR2Update, BlockMetadata]>>} The array of BTCR2 Signed Updates announced by the Beacon Signals.
|
|
211
213
|
*/
|
|
212
214
|
static async beaconSignals(
|
|
213
215
|
beaconServices: Array<BeaconService>,
|
|
214
216
|
sidecarData: SidecarData,
|
|
215
|
-
bitcoin:
|
|
217
|
+
bitcoin: BitcoinConnection
|
|
216
218
|
): Promise<Array<[SignedBTCR2Update, BlockMetadata]>> {
|
|
217
219
|
// Discover Beacon Signals via indexer server (esplora/electrs) or full node
|
|
218
|
-
const beaconServicesSignals = bitcoin.
|
|
220
|
+
const beaconServicesSignals = bitcoin.rest
|
|
219
221
|
? await BeaconSignalDiscovery.indexer(beaconServices, bitcoin)
|
|
220
222
|
: await BeaconSignalDiscovery.fullnode(beaconServices, bitcoin);
|
|
221
223
|
|
|
222
224
|
// Process each beacon's signals in parallel
|
|
223
|
-
const promises =
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
225
|
+
const promises = await Promise.all(
|
|
226
|
+
Array.from(beaconServicesSignals.entries()).map(
|
|
227
|
+
async ([service, signals]) => {
|
|
228
|
+
// Skip beacons with no signals
|
|
229
|
+
if (!signals.length) return [];
|
|
230
|
+
// Establish a typed beacon and process its signals
|
|
231
|
+
return BeaconFactory.establish(service).processSignals(signals, sidecarData);
|
|
232
|
+
}
|
|
233
|
+
)
|
|
230
234
|
);
|
|
231
235
|
|
|
232
|
-
return
|
|
236
|
+
return promises.flat();
|
|
233
237
|
}
|
|
234
238
|
|
|
235
239
|
/**
|
|
@@ -258,31 +262,36 @@ export class Resolve {
|
|
|
258
262
|
);
|
|
259
263
|
|
|
260
264
|
// Create a default response object
|
|
261
|
-
const response = {
|
|
262
|
-
currentDocument,
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
265
|
+
const response: DidResolutionResponse = {
|
|
266
|
+
didDocument : currentDocument,
|
|
267
|
+
metadata : {
|
|
268
|
+
versionId : `${currentVersionId}`,
|
|
269
|
+
confirmations : 0,
|
|
270
|
+
updated : '',
|
|
271
|
+
deactivated : currentDocument.deactivated || false
|
|
272
|
+
}
|
|
266
273
|
};
|
|
267
274
|
|
|
268
275
|
// Iterate over each (update block) pair
|
|
269
276
|
for(const [update, block] of updates) {
|
|
270
277
|
// Get the hash of the current document
|
|
271
|
-
const currentDocumentHash =
|
|
278
|
+
const currentDocumentHash = Canonicalization.process(response.didDocument, { encoding: 'base58btc' });
|
|
272
279
|
|
|
273
|
-
//
|
|
274
|
-
|
|
280
|
+
// Safely convert block.time to timestamp
|
|
281
|
+
const blocktime = DateUtils.blocktimeToTimestamp(block.time);
|
|
275
282
|
|
|
276
|
-
// Safely convert versionTime to timestamp
|
|
277
|
-
const vTime = DateUtils.dateStringToTimestamp(versionTime || '');
|
|
278
|
-
// Safely convert block.blocktime to timestamp
|
|
279
|
-
const bTime = DateUtils.blocktimeToTimestamp(block.time);
|
|
280
283
|
// Set the updated field to the blocktime of the current update
|
|
281
|
-
response.updated = DateUtils.toISOStringNonFractional(
|
|
284
|
+
response.metadata.updated = DateUtils.toISOStringNonFractional(blocktime);
|
|
285
|
+
|
|
286
|
+
// Set confirmations to the block confirmations
|
|
287
|
+
response.metadata.confirmations = block.confirmations;
|
|
282
288
|
|
|
283
289
|
// if resolutionOptions.versionTime is defined and the blocktime is more recent, return currentDocument
|
|
284
|
-
if(
|
|
285
|
-
|
|
290
|
+
if(versionTime) {
|
|
291
|
+
// Safely convert versionTime to timestamp
|
|
292
|
+
if(blocktime > DateUtils.dateStringToTimestamp(versionTime)) {
|
|
293
|
+
return response;
|
|
294
|
+
}
|
|
286
295
|
}
|
|
287
296
|
|
|
288
297
|
// Check update.targetVersionId against currentVersionId
|
|
@@ -294,23 +303,21 @@ export class Resolve {
|
|
|
294
303
|
|
|
295
304
|
// If update.targetVersionId == currentVersionId + 1, apply the update
|
|
296
305
|
else if (update.targetVersionId === currentVersionId + 1) {
|
|
297
|
-
// Prepend `z` to the sourceHash if it does not start with it
|
|
298
|
-
const sourceHash = update.sourceHash.startsWith('z') ? update.sourceHash : `z${update.sourceHash}`;
|
|
299
|
-
|
|
300
306
|
// Check if update.sourceHash !== currentDocumentHash
|
|
301
|
-
if (sourceHash !== currentDocumentHash) {
|
|
307
|
+
if (update.sourceHash !== currentDocumentHash) {
|
|
302
308
|
// Raise an INVALID_DID_UPDATE error if they do not match
|
|
303
309
|
throw new ResolveError(
|
|
304
310
|
`Hash mismatch: update.sourceHash !== currentDocumentHash`,
|
|
305
|
-
INVALID_DID_UPDATE, { sourceHash, currentDocumentHash }
|
|
311
|
+
INVALID_DID_UPDATE, { sourceHash: update.sourceHash, currentDocumentHash }
|
|
306
312
|
);
|
|
307
313
|
}
|
|
308
314
|
// Apply the update to the currentDocument and set it in the response
|
|
309
|
-
response.
|
|
315
|
+
response.didDocument = await this.applyUpdate(response.didDocument, update);
|
|
316
|
+
|
|
310
317
|
// Create unsigned_update by removing the proof property from update.
|
|
311
318
|
const unsignedUpdate = JSONUtils.deleteKeys(update, ['proof']) as UnsignedBTCR2Update;
|
|
312
319
|
// Push the canonicalized unsigned update hash to the updateHashHistory
|
|
313
|
-
updateHashHistory.push(
|
|
320
|
+
updateHashHistory.push(Canonicalization.process(unsignedUpdate, { encoding: 'base58btc' }));
|
|
314
321
|
}
|
|
315
322
|
|
|
316
323
|
// If update.targetVersionId > currentVersionId + 1, throw LATE_PUBLISHING error
|
|
@@ -319,15 +326,16 @@ export class Resolve {
|
|
|
319
326
|
`Version Id Mismatch: targetVersionId cannot be > currentVersionId + 1`,
|
|
320
327
|
'LATE_PUBLISHING_ERROR', {
|
|
321
328
|
targetVersionId : update.targetVersionId,
|
|
322
|
-
currentVersionId :
|
|
329
|
+
currentVersionId : currentVersionId + 1
|
|
323
330
|
}
|
|
324
331
|
);
|
|
325
332
|
}
|
|
326
333
|
|
|
327
334
|
// Increment currentVersionId
|
|
328
335
|
currentVersionId++;
|
|
329
|
-
|
|
330
|
-
response.versionId
|
|
336
|
+
|
|
337
|
+
// Set response.versionId to be the new currentVersionId
|
|
338
|
+
response.metadata.versionId = `${currentVersionId}`;
|
|
331
339
|
|
|
332
340
|
// If resolutionOptions.versionId is defined and <= currentVersionId, return currentDocument
|
|
333
341
|
if(currentVersionId >= Number(versionId)) {
|
|
@@ -336,6 +344,9 @@ export class Resolve {
|
|
|
336
344
|
|
|
337
345
|
// Check if the current document is deactivated before further processing
|
|
338
346
|
if(currentDocument.deactivated) {
|
|
347
|
+
// Set the response deactivated flag to true
|
|
348
|
+
response.metadata.deactivated = currentDocument.deactivated;
|
|
349
|
+
// If deactivated, stop processing further updates and return the response
|
|
339
350
|
return response;
|
|
340
351
|
}
|
|
341
352
|
}
|
|
@@ -352,12 +363,12 @@ export class Resolve {
|
|
|
352
363
|
*/
|
|
353
364
|
static confirmDuplicate(update: SignedBTCR2Update, updateHashHistory: string[]): void {
|
|
354
365
|
// Create unsigned_update by removing the proof property from update.
|
|
355
|
-
const
|
|
366
|
+
const { proof: _, ...unsignedUpdate } = update;
|
|
356
367
|
|
|
357
368
|
// Hash unsignedUpdate with JSON Document Hashing algorithm
|
|
358
|
-
const unsignedUpdateHash =
|
|
369
|
+
const unsignedUpdateHash = Canonicalization.process(unsignedUpdate);
|
|
359
370
|
|
|
360
|
-
//
|
|
371
|
+
// Let historicalUpdateHash equal updateHashHistory[updateHashIndex].
|
|
361
372
|
const historicalUpdateHash = updateHashHistory[update.targetVersionId - 2];
|
|
362
373
|
|
|
363
374
|
// Check if the updateHash matches the historical hash
|
|
@@ -420,7 +431,7 @@ export class Resolve {
|
|
|
420
431
|
const cryptosuite = new BIP340Cryptosuite(multikey);
|
|
421
432
|
|
|
422
433
|
// Canonicalize the update
|
|
423
|
-
const canonicalUpdate =
|
|
434
|
+
const canonicalUpdate = Canonicalization.canonicalize(update);
|
|
424
435
|
|
|
425
436
|
// Construct a DataIntegrityProof with the cryptosuite
|
|
426
437
|
const diProof = new BIP340DataIntegrityProof(cryptosuite);
|
|
@@ -443,16 +454,16 @@ export class Resolve {
|
|
|
443
454
|
DidDocument.validate(updatedDocument);
|
|
444
455
|
|
|
445
456
|
// Canonicalize and hash the updatedDocument to get the currentDocumentHash.
|
|
446
|
-
const currentDocumentHash =
|
|
457
|
+
const currentDocumentHash = Canonicalization.process(updatedDocument, { encoding: 'base58btc' });
|
|
447
458
|
|
|
448
459
|
// Prepare the update targetHash for comparison with currentDocumentHash.
|
|
449
|
-
const updateTargetHash = update.targetHash
|
|
460
|
+
const updateTargetHash = update.targetHash;
|
|
450
461
|
|
|
451
462
|
// Make sure the update.targetHash equals currentDocumentHash.
|
|
452
|
-
if (
|
|
463
|
+
if (update.targetHash !== currentDocumentHash) {
|
|
453
464
|
// If they do not match, throw INVALID_DID_UPDATE error.
|
|
454
465
|
throw new ResolveError(
|
|
455
|
-
`Invalid update:
|
|
466
|
+
`Invalid update: update.targetHash !== currentDocumentHash`,
|
|
456
467
|
INVALID_DID_UPDATE, { updateTargetHash, currentDocumentHash }
|
|
457
468
|
);
|
|
458
469
|
}
|
package/src/core/update.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
Canonicalization,
|
|
2
3
|
INVALID_DID_UPDATE,
|
|
3
4
|
JSONPatch,
|
|
4
5
|
KeyBytes,
|
|
@@ -11,11 +12,10 @@ import {
|
|
|
11
12
|
SignedBTCR2Update,
|
|
12
13
|
UnsignedBTCR2Update
|
|
13
14
|
} from '@did-btcr2/cryptosuite';
|
|
14
|
-
import { canonicalization } from '../did-btcr2.js';
|
|
15
15
|
import { Btcr2DidDocument, DidDocument, DidVerificationMethod } from '../utils/did-document.js';
|
|
16
16
|
import { BeaconFactory } from './beacon/factory.js';
|
|
17
17
|
import { BeaconService } from './beacon/interfaces.js';
|
|
18
|
-
import {
|
|
18
|
+
import { BitcoinConnection } from '@did-btcr2/bitcoin';
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Implements {@link https://dcdpr.github.io/did-btcr2/operations/update.html | 7.3 Update}.
|
|
@@ -56,7 +56,7 @@ export class Update {
|
|
|
56
56
|
patch : patches,
|
|
57
57
|
targetHash : '',
|
|
58
58
|
targetVersionId : sourceVersionId + 1,
|
|
59
|
-
sourceHash :
|
|
59
|
+
sourceHash : Canonicalization.process(sourceDocument, { encoding: 'base58btc' }),
|
|
60
60
|
};
|
|
61
61
|
|
|
62
62
|
// Apply all JSON patches to sourceDocument.
|
|
@@ -74,7 +74,7 @@ export class Update {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
// Set the targetHash by canonicalizing the targetDocument and encoding it in base58.
|
|
77
|
-
unsignedUpdate.targetHash =
|
|
77
|
+
unsignedUpdate.targetHash = Canonicalization.process(targetDocument, { encoding: 'base58btc' });
|
|
78
78
|
|
|
79
79
|
// Return unsignedUpdate.
|
|
80
80
|
return unsignedUpdate;
|
|
@@ -140,7 +140,7 @@ export class Update {
|
|
|
140
140
|
beaconService: BeaconService,
|
|
141
141
|
update: SignedBTCR2Update,
|
|
142
142
|
secretKey: KeyBytes,
|
|
143
|
-
bitcoin:
|
|
143
|
+
bitcoin: BitcoinConnection
|
|
144
144
|
): Promise<SignedBTCR2Update> {
|
|
145
145
|
// Establish a beacon object
|
|
146
146
|
const beacon = BeaconFactory.establish(beaconService);
|
package/src/did-btcr2.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BitcoinConnection } from '@did-btcr2/bitcoin';
|
|
2
2
|
import {
|
|
3
|
-
Canonicalization,
|
|
4
3
|
DocumentBytes,
|
|
5
4
|
HexString,
|
|
6
5
|
IdentifierHrp,
|
|
@@ -9,6 +8,7 @@ import {
|
|
|
9
8
|
KeyBytes,
|
|
10
9
|
METHOD_NOT_SUPPORTED,
|
|
11
10
|
MethodError,
|
|
11
|
+
MISSING_RESOLUTION_OPTIONS,
|
|
12
12
|
MISSING_UPDATE_DATA,
|
|
13
13
|
PatchOperation,
|
|
14
14
|
ResolveError,
|
|
@@ -34,14 +34,11 @@ import { Update } from './core/update.js';
|
|
|
34
34
|
import { Appendix } from './utils/appendix.js';
|
|
35
35
|
import { Btcr2DidDocument, DidVerificationMethod } from './utils/did-document.js';
|
|
36
36
|
|
|
37
|
-
// TODO: convert to API driver?
|
|
38
|
-
export const canonicalization = new Canonicalization();
|
|
39
|
-
|
|
40
37
|
export type Btcr2Identifier = string;
|
|
41
38
|
|
|
42
39
|
export interface DidCreateOptions {
|
|
43
40
|
/** Type of identifier to create (key or external) */
|
|
44
|
-
idType:
|
|
41
|
+
idType: string;
|
|
45
42
|
/** DID BTCR2 Version Number */
|
|
46
43
|
version?: number;
|
|
47
44
|
/** Bitcoin Network */
|
|
@@ -81,11 +78,13 @@ export class DidBtcr2 implements DidMethod {
|
|
|
81
78
|
* @param {string} options.network The Bitcoin network to use for the identifier, e.g. 'bitcoin', 'testnet', etc. Defaults to 'bitcoin'.
|
|
82
79
|
* @returns {Promise<Btcr2Identifier>} Promise resolving to a Btcr2Identifier string.
|
|
83
80
|
* @throws {MethodError} if any of the checks fail
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* const genesisBytes = SchnorrKeyPair.generate().publicKey.compressed;
|
|
84
|
+
* const did = DidBtcr2.create(genesisBytes, { idType: 'KEY', network: 'regtest' });
|
|
85
|
+
* ```
|
|
84
86
|
*/
|
|
85
|
-
static
|
|
86
|
-
genesisBytes: KeyBytes | DocumentBytes,
|
|
87
|
-
options?: DidCreateOptions
|
|
88
|
-
): Promise<Btcr2Identifier> {
|
|
87
|
+
static create(genesisBytes: KeyBytes | DocumentBytes, options?: DidCreateOptions): Btcr2Identifier {
|
|
89
88
|
// Deconstruct the idType, version and network from the options, setting defaults if not given
|
|
90
89
|
const { idType, version = 1, network = 'bitcoin' } = options || {};
|
|
91
90
|
|
|
@@ -97,7 +96,7 @@ export class DidBtcr2 implements DidMethod {
|
|
|
97
96
|
}
|
|
98
97
|
|
|
99
98
|
// Call identifier encoding algorithm
|
|
100
|
-
return Identifier.encode({ idType,
|
|
99
|
+
return Identifier.encode(genesisBytes, { idType, version, network });
|
|
101
100
|
}
|
|
102
101
|
|
|
103
102
|
/**
|
|
@@ -117,24 +116,26 @@ export class DidBtcr2 implements DidMethod {
|
|
|
117
116
|
* @example
|
|
118
117
|
* ```ts
|
|
119
118
|
* const resolution = await DidBtcr2.resolve(
|
|
120
|
-
* 'did:btcr2:
|
|
119
|
+
* 'did:btcr2:k1qgpr45cheptyjekl3cex80xfnkwhxnlclecwwf92gvdjrszm2uwhzlcxu5xte'
|
|
121
120
|
* )
|
|
122
121
|
* ```
|
|
123
122
|
*/
|
|
124
123
|
static async resolve(
|
|
125
124
|
did: string,
|
|
126
|
-
resolutionOptions: ResolutionOptions = {
|
|
125
|
+
resolutionOptions: ResolutionOptions = {}
|
|
127
126
|
): Promise<DidResolutionResult> {
|
|
128
127
|
try {
|
|
128
|
+
// Set versionId from resolutionOptions
|
|
129
|
+
const versionId = resolutionOptions.versionId;
|
|
129
130
|
|
|
130
131
|
// Initialize an empty DID Resolution Result
|
|
131
132
|
const didResolutionResult: DidResolutionResult = {
|
|
132
133
|
'@context' : 'https://w3id.org/did-resolution/v1',
|
|
133
134
|
didResolutionMetadata : { contentType: 'application/ld+json' },
|
|
134
135
|
didDocumentMetadata : {
|
|
136
|
+
versionId,
|
|
135
137
|
deactivated : false,
|
|
136
138
|
updated : undefined,
|
|
137
|
-
versionId : resolutionOptions.versionId,
|
|
138
139
|
confirmations : undefined,
|
|
139
140
|
},
|
|
140
141
|
didDocument : null,
|
|
@@ -146,25 +147,16 @@ export class DidBtcr2 implements DidMethod {
|
|
|
146
147
|
// Process sidecar if provided
|
|
147
148
|
const sidecarData = Resolve.sidecarData(resolutionOptions.sidecar);
|
|
148
149
|
|
|
149
|
-
// Check if bitcoin driver provided
|
|
150
|
-
if(!resolutionOptions?.drivers?.bitcoin) {
|
|
151
|
-
// If not, initialize default drivers
|
|
152
|
-
resolutionOptions.drivers = resolutionOptions.drivers || {};
|
|
153
|
-
// Set bitcoin driver to default BitcoinNetworkConnection
|
|
154
|
-
resolutionOptions.drivers.bitcoin = new BitcoinNetworkConnection();
|
|
155
|
-
// Set the network based on the decoded identifier
|
|
156
|
-
resolutionOptions.drivers.bitcoin.setActiveNetwork(didComponents.network);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
150
|
// Parse the genesis document from the resolution options if provided
|
|
160
151
|
const genesisDocument = resolutionOptions.sidecar?.genesisDocument;
|
|
152
|
+
|
|
161
153
|
// Since genesisDocument is optional, check if it exists
|
|
162
154
|
if(!genesisDocument) {
|
|
163
155
|
// If no genesisDocument and x HRP, throw MISSING_UPDATE_DATA error
|
|
164
156
|
if(didComponents.hrp === IdentifierHrp.x)
|
|
165
157
|
throw new ResolveError(
|
|
166
158
|
'External resolution requires genesisDocument',
|
|
167
|
-
MISSING_UPDATE_DATA,
|
|
159
|
+
MISSING_UPDATE_DATA, resolutionOptions
|
|
168
160
|
);
|
|
169
161
|
}
|
|
170
162
|
|
|
@@ -176,23 +168,32 @@ export class DidBtcr2 implements DidMethod {
|
|
|
176
168
|
.filter(BeaconUtils.isBeaconService)
|
|
177
169
|
.map(BeaconUtils.parseBeaconServiceEndpoint);
|
|
178
170
|
|
|
179
|
-
|
|
171
|
+
// Check if bitcoin driver provided
|
|
172
|
+
if(!resolutionOptions?.drivers?.bitcoin) {
|
|
173
|
+
throw new ResolveError(
|
|
174
|
+
'Bitcoin connection required for resolve. Pass a configured driver via '
|
|
175
|
+
+ 'resolutionOptions.drivers.bitcoin or use DidBtcr2Api which injects it automatically.',
|
|
176
|
+
MISSING_RESOLUTION_OPTIONS, resolutionOptions
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Get the bitcoin driver from the resolution options
|
|
181
|
+
const bitcoin = resolutionOptions.drivers.bitcoin;
|
|
180
182
|
|
|
181
183
|
// Process the Beacon Signals to get the required updates
|
|
182
184
|
const unsortedUpdates = await Resolve.beaconSignals(
|
|
183
185
|
beaconServices,
|
|
184
186
|
sidecarData,
|
|
185
|
-
|
|
187
|
+
bitcoin
|
|
186
188
|
);
|
|
187
|
-
console.log('unsortedUpdates', unsortedUpdates);
|
|
188
189
|
|
|
189
190
|
// If no updates found, return the current document
|
|
190
191
|
if(!unsortedUpdates.length) {
|
|
191
|
-
// Set the
|
|
192
|
+
// Set the didDocument in didResolutionResult based on currentDocument
|
|
192
193
|
didResolutionResult.didDocument = currentDocument;
|
|
193
|
-
|
|
194
|
-
// Set the deactivated status in the didDocumentMetadata
|
|
194
|
+
// Set other required fields in the didResolutionResult
|
|
195
195
|
didResolutionResult.didDocumentMetadata.deactivated = !!currentDocument.deactivated;
|
|
196
|
+
didResolutionResult.didDocumentMetadata.versionId = versionId ?? '1';
|
|
196
197
|
|
|
197
198
|
// Return the didResolutionResult early
|
|
198
199
|
return didResolutionResult;
|
|
@@ -203,21 +204,18 @@ export class DidBtcr2 implements DidMethod {
|
|
|
203
204
|
currentDocument,
|
|
204
205
|
unsortedUpdates,
|
|
205
206
|
resolutionOptions.versionTime,
|
|
206
|
-
|
|
207
|
+
versionId
|
|
207
208
|
);
|
|
208
209
|
|
|
209
210
|
// Set all of the required fields in the didResolutionResult
|
|
210
|
-
didResolutionResult.didDocument = result.
|
|
211
|
-
didResolutionResult.didDocumentMetadata
|
|
212
|
-
didResolutionResult.didDocumentMetadata.versionId = result.versionId;
|
|
213
|
-
didResolutionResult.didDocumentMetadata.deactivated = !!result.currentDocument.deactivated;
|
|
211
|
+
didResolutionResult.didDocument = result.didDocument;
|
|
212
|
+
didResolutionResult.didDocumentMetadata = result.metadata;
|
|
214
213
|
|
|
215
214
|
// Return didResolutionResult;
|
|
216
215
|
return didResolutionResult;
|
|
217
216
|
} catch (error: any) {
|
|
218
|
-
console.error(error);
|
|
219
217
|
// Rethrow any unexpected errors that are not a `ResolveError`.
|
|
220
|
-
if (!(error instanceof ResolveError)) throw new Error(error);
|
|
218
|
+
if (!(error instanceof ResolveError)) throw new Error(error.message ?? error, { cause: error });
|
|
221
219
|
|
|
222
220
|
// Return a DID Resolution Result with the appropriate error code.
|
|
223
221
|
return {
|
|
@@ -245,7 +243,7 @@ export class DidBtcr2 implements DidMethod {
|
|
|
245
243
|
* @param {string} params.verificationMethodId The verificationMethod ID to sign the update with.
|
|
246
244
|
* @param {string} params.beaconId The beacon ID associated with the update.
|
|
247
245
|
* @param {KeyBytes | HexString} [params.signingMaterial] Optional signing material (key bytes or hex string).
|
|
248
|
-
* @param {
|
|
246
|
+
* @param {BitcoinConnection} [params.bitcoin] Optional Bitcoin network connection for announcing the update. If not provided, a default connection will be initialized.
|
|
249
247
|
* @return {Promise<SignedBTCR2Update>} Promise resolving to the signed BTCR2 update.
|
|
250
248
|
* @throws {UpdateError} if no verificationMethod, verificationMethod type is not `Multikey` or the publicKeyMultibase
|
|
251
249
|
* header is not `zQ3s`
|
|
@@ -265,7 +263,7 @@ export class DidBtcr2 implements DidMethod {
|
|
|
265
263
|
verificationMethodId: string;
|
|
266
264
|
beaconId: string;
|
|
267
265
|
signingMaterial?: KeyBytes | HexString;
|
|
268
|
-
bitcoin?:
|
|
266
|
+
bitcoin?: BitcoinConnection;
|
|
269
267
|
}): Promise<SignedBTCR2Update> {
|
|
270
268
|
// TODO: provide KMS as alternative
|
|
271
269
|
// If no signingMaterial provided, throw an UpdateError with INVALID_DID_UPDATE.
|
|
@@ -335,11 +333,17 @@ export class DidBtcr2 implements DidMethod {
|
|
|
335
333
|
INVALID_DID_UPDATE, {sourceDocument, beaconId}
|
|
336
334
|
);
|
|
337
335
|
}
|
|
338
|
-
//
|
|
339
|
-
|
|
336
|
+
// Require an explicit bitcoin connection — no silent env-var fallback
|
|
337
|
+
if (!bitcoin) {
|
|
338
|
+
throw new UpdateError(
|
|
339
|
+
'Bitcoin connection required for update. Pass a configured `bitcoin` parameter '
|
|
340
|
+
+ 'or use DidBtcr2Api which injects it automatically.',
|
|
341
|
+
INVALID_DID_UPDATE, { beaconId }
|
|
342
|
+
);
|
|
343
|
+
}
|
|
340
344
|
|
|
341
345
|
// Announce the signed update to the blockchain using the specified beacon(s)
|
|
342
|
-
|
|
346
|
+
await Update.announce(beaconService, signed, secretKey, bitcoin);
|
|
343
347
|
|
|
344
348
|
// Return signed update if announced successfully
|
|
345
349
|
return signed;
|
|
@@ -22,13 +22,6 @@ export class DidDocumentBuilder {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
withController(controller?: Array<string>): this {
|
|
26
|
-
if (controller) {
|
|
27
|
-
this.document.controller = controller ?? [this.document.id!];
|
|
28
|
-
}
|
|
29
|
-
return this;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
25
|
withAuthentication(authentication: Array<string | DidVerificationMethod>): this {
|
|
33
26
|
if (authentication) {
|
|
34
27
|
this.document.authentication = authentication;
|