@did-btcr2/method 0.20.0 → 0.22.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 +3316 -3292
- package/dist/browser.mjs +3316 -3292
- package/dist/cjs/core/beacon/singleton.js +6 -9
- package/dist/cjs/core/beacon/singleton.js.map +1 -1
- package/dist/cjs/core/resolve.js +41 -37
- 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 +32 -28
- 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 +31 -24
- package/dist/cjs/utils/did-document.js.map +1 -1
- package/dist/esm/core/beacon/singleton.js +6 -9
- package/dist/esm/core/beacon/singleton.js.map +1 -1
- package/dist/esm/core/resolve.js +41 -37
- 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 +32 -28
- 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 +31 -24
- package/dist/esm/utils/did-document.js.map +1 -1
- package/dist/types/core/beacon/singleton.d.ts +1 -1
- package/dist/types/core/beacon/singleton.d.ts.map +1 -1
- package/dist/types/core/resolve.d.ts +7 -4
- package/dist/types/core/resolve.d.ts.map +1 -1
- package/dist/types/core/update.d.ts.map +1 -1
- package/dist/types/did-btcr2.d.ts +8 -4
- 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 +12 -9
- package/dist/types/utils/did-document.d.ts.map +1 -1
- package/package.json +7 -6
- package/src/core/beacon/singleton.ts +7 -9
- package/src/core/resolve.ts +59 -47
- package/src/core/update.ts +3 -3
- package/src/did-btcr2.ts +34 -32
- package/src/utils/did-document-builder.ts +0 -7
- package/src/utils/did-document.ts +41 -30
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@did-btcr2/method",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Reference implementation for the did:btcr2 DID method written in TypeScript and JavaScript. did:btcr2 is a censorship resistant DID Method using the Bitcoin blockchain as a Verifiable Data Registry to announce changes to the DID document. This is the core method implementation for the did-btcr2-js monorepo.",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
7
7
|
"module": "./dist/esm/index.js",
|
|
8
8
|
"types": "./dist/types/index.d.ts",
|
|
@@ -83,11 +83,11 @@
|
|
|
83
83
|
"multiformats": "^13.3.1",
|
|
84
84
|
"nostr-tools": "^2.15.0",
|
|
85
85
|
"tiny-secp256k1": "^2.2.3",
|
|
86
|
-
"@did-btcr2/cryptosuite": "5.0.0",
|
|
87
86
|
"@did-btcr2/bitcoin": "0.3.4",
|
|
87
|
+
"@did-btcr2/cryptosuite": "5.1.0",
|
|
88
88
|
"@did-btcr2/kms": "0.2.0",
|
|
89
|
-
"@did-btcr2/
|
|
90
|
-
"@did-btcr2/
|
|
89
|
+
"@did-btcr2/keypair": "0.9.1",
|
|
90
|
+
"@did-btcr2/common": "4.0.1"
|
|
91
91
|
},
|
|
92
92
|
"devDependencies": {
|
|
93
93
|
"@eslint/js": "^9.22.0",
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"@types/chai-as-promised": "^8.0.1",
|
|
96
96
|
"@types/eslint": "^9.6.1",
|
|
97
97
|
"@types/mocha": "^10.0.9",
|
|
98
|
-
"@types/node": "^
|
|
98
|
+
"@types/node": "^25.3.0",
|
|
99
99
|
"@typescript-eslint/eslint-plugin": "^8.5.0",
|
|
100
100
|
"@typescript-eslint/parser": "^8.5.0",
|
|
101
101
|
"c8": "^10.1.2",
|
|
@@ -138,6 +138,7 @@
|
|
|
138
138
|
"build:test": "pnpm build && pnpm build:tests && pnpm c8 mocha",
|
|
139
139
|
"build:lint:test": "pnpm build && pnpm build:tests && pnpm lint:fix",
|
|
140
140
|
"prepublish": "pnpm build",
|
|
141
|
+
"generate:vector": "pnpm tsx lib/generate-vector.ts",
|
|
141
142
|
"do": "pnpm tsx",
|
|
142
143
|
"do:lib": "${PWD}/lib/run-lib.sh",
|
|
143
144
|
"pack:local": "pnpm pack && mv *.tgz ./release"
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { AddressUtxo, BitcoinNetworkConnection } from '@did-btcr2/bitcoin';
|
|
2
|
-
import { INVALID_SIDECAR_DATA, KeyBytes, MISSING_UPDATE_DATA } from '@did-btcr2/common';
|
|
2
|
+
import { Canonicalization, INVALID_SIDECAR_DATA, KeyBytes, MISSING_UPDATE_DATA } from '@did-btcr2/common';
|
|
3
3
|
import { SignedBTCR2Update } from '@did-btcr2/cryptosuite';
|
|
4
4
|
import { SchnorrKeyPair } from '@did-btcr2/keypair';
|
|
5
5
|
import { Signer } from '@did-btcr2/kms';
|
|
6
6
|
import { opcodes, Psbt, script } from 'bitcoinjs-lib';
|
|
7
7
|
import { base58btc } from 'multiformats/bases/base58';
|
|
8
|
-
import { canonicalization } from '../../did-btcr2.js';
|
|
9
8
|
import { SidecarData } from '../types.js';
|
|
10
9
|
import { Beacon } from './beacon.js';
|
|
11
10
|
import { SingletonBeaconError } from './error.js';
|
|
@@ -15,7 +14,7 @@ import { BeaconService, BeaconSignal, BlockMetadata } from './interfaces.js';
|
|
|
15
14
|
* Implements {@link https://dcdpr.github.io/did-btcr2/terminology.html#singleton-beacon | Singleton Beacon}.
|
|
16
15
|
* @class SingletonBeacon
|
|
17
16
|
* @type {SingletonBeacon}
|
|
18
|
-
* @extends {
|
|
17
|
+
* @extends {Beacon}
|
|
19
18
|
*/
|
|
20
19
|
export class SingletonBeacon extends Beacon {
|
|
21
20
|
|
|
@@ -57,7 +56,7 @@ export class SingletonBeacon extends Beacon {
|
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
// Canonicalize, hash and encode to base58 the signed update object found in sidecar or CAS
|
|
60
|
-
const encodedUpdate =
|
|
59
|
+
const encodedUpdate = Canonicalization.process(signedUpdate, { encoding: 'base58' });
|
|
61
60
|
|
|
62
61
|
// Encode the signal bytes hex string to base58
|
|
63
62
|
const signalBytes = base58btc.encode(Buffer.from(updateHash, 'hex'));
|
|
@@ -122,7 +121,7 @@ export class SingletonBeacon extends Beacon {
|
|
|
122
121
|
const prevTx = await bitcoin.network.rest.transaction.getHex(utxo.txid);
|
|
123
122
|
|
|
124
123
|
// Canonicalize and hash the signed update for OP_RETURN output
|
|
125
|
-
const updateHash =
|
|
124
|
+
const updateHash = Canonicalization.andHash(signedUpdate);
|
|
126
125
|
|
|
127
126
|
// Construct a spend transaction
|
|
128
127
|
const spendTx = new Psbt({ network: bitcoin.network.data })
|
|
@@ -140,9 +139,6 @@ export class SingletonBeacon extends Beacon {
|
|
|
140
139
|
|
|
141
140
|
// Construct a Schnorr key pair from the secret key
|
|
142
141
|
const keyPair = SchnorrKeyPair.fromSecret(secretKey);
|
|
143
|
-
if (!keyPair) {
|
|
144
|
-
throw new SingletonBeaconError('Key pair not found.', 'KEY_PAIR_NOT_FOUND', { secretKey });
|
|
145
|
-
}
|
|
146
142
|
|
|
147
143
|
// Construct a signer object from the key pair and bitcoin network
|
|
148
144
|
const signer = new Signer({ keyPair, network: bitcoin.network.name });
|
|
@@ -155,7 +151,9 @@ export class SingletonBeacon extends Beacon {
|
|
|
155
151
|
|
|
156
152
|
// Broadcast spendTx to the Bitcoin network.
|
|
157
153
|
const txid = await bitcoin.network.rest.transaction.send(signedTx);
|
|
158
|
-
|
|
154
|
+
|
|
155
|
+
// Log the txid of the broadcasted transaction
|
|
156
|
+
console.info(`Singleton Beacon Signal Broadcasted with txid: ${txid}`);
|
|
159
157
|
|
|
160
158
|
// Return the signed update
|
|
161
159
|
return signedUpdate;
|
package/src/core/resolve.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
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
|
/**
|
|
@@ -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,7 +105,7 @@ 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
110
|
// Compare the genesisBytes to the hashBytes
|
|
108
111
|
const genesisBytes = bytesToHex(didComponents.genesisBytes);
|
|
@@ -137,14 +140,14 @@ export class Resolve {
|
|
|
137
140
|
const updateMap = new Map<string, SignedBTCR2Update>();
|
|
138
141
|
if(sidecar.updates?.length)
|
|
139
142
|
for(const update of sidecar.updates) {
|
|
140
|
-
updateMap.set(
|
|
143
|
+
updateMap.set(Canonicalization.process(update, { encoding: 'hex' }), update);
|
|
141
144
|
}
|
|
142
145
|
|
|
143
146
|
// CAS Announcements map
|
|
144
147
|
const casMap = new Map<string, CASAnnouncement>();
|
|
145
148
|
if(sidecar.casUpdates?.length)
|
|
146
149
|
for(const update of sidecar.casUpdates) {
|
|
147
|
-
casMap.set(
|
|
150
|
+
casMap.set(Canonicalization.process(update, { encoding: 'hex' }), update);
|
|
148
151
|
}
|
|
149
152
|
|
|
150
153
|
// SMT Proofs map
|
|
@@ -220,16 +223,18 @@ export class Resolve {
|
|
|
220
223
|
: await BeaconSignalDiscovery.fullnode(beaconServices, bitcoin);
|
|
221
224
|
|
|
222
225
|
// Process each beacon's signals in parallel
|
|
223
|
-
const promises =
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
226
|
+
const promises = await Promise.all(
|
|
227
|
+
Array.from(beaconServicesSignals.entries()).map(
|
|
228
|
+
async ([service, signals]) => {
|
|
229
|
+
// Skip beacons with no signals
|
|
230
|
+
if (!signals.length) return [];
|
|
231
|
+
// Establish a typed beacon and process its signals
|
|
232
|
+
return BeaconFactory.establish(service).processSignals(signals, sidecarData);
|
|
233
|
+
}
|
|
234
|
+
)
|
|
230
235
|
);
|
|
231
236
|
|
|
232
|
-
return
|
|
237
|
+
return promises.flat();
|
|
233
238
|
}
|
|
234
239
|
|
|
235
240
|
/**
|
|
@@ -258,31 +263,36 @@ export class Resolve {
|
|
|
258
263
|
);
|
|
259
264
|
|
|
260
265
|
// Create a default response object
|
|
261
|
-
const response = {
|
|
262
|
-
currentDocument,
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
+
const response: DidResolutionResponse = {
|
|
267
|
+
didDocument : currentDocument,
|
|
268
|
+
metadata : {
|
|
269
|
+
versionId : `${currentVersionId}`,
|
|
270
|
+
confirmations : 0,
|
|
271
|
+
updated : '',
|
|
272
|
+
deactivated : currentDocument.deactivated || false
|
|
273
|
+
}
|
|
266
274
|
};
|
|
267
275
|
|
|
268
276
|
// Iterate over each (update block) pair
|
|
269
277
|
for(const [update, block] of updates) {
|
|
270
278
|
// Get the hash of the current document
|
|
271
|
-
const currentDocumentHash =
|
|
279
|
+
const currentDocumentHash = Canonicalization.process(response.didDocument, { encoding: 'base58' });
|
|
272
280
|
|
|
273
|
-
//
|
|
274
|
-
|
|
281
|
+
// Safely convert block.time to timestamp
|
|
282
|
+
const blocktime = DateUtils.blocktimeToTimestamp(block.time);
|
|
275
283
|
|
|
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
284
|
// Set the updated field to the blocktime of the current update
|
|
281
|
-
response.updated = DateUtils.toISOStringNonFractional(
|
|
285
|
+
response.metadata.updated = DateUtils.toISOStringNonFractional(blocktime);
|
|
286
|
+
|
|
287
|
+
// Set confirmations to the block confirmations
|
|
288
|
+
response.metadata.confirmations = block.confirmations;
|
|
282
289
|
|
|
283
290
|
// if resolutionOptions.versionTime is defined and the blocktime is more recent, return currentDocument
|
|
284
|
-
if(
|
|
285
|
-
|
|
291
|
+
if(versionTime) {
|
|
292
|
+
// Safely convert versionTime to timestamp
|
|
293
|
+
if(blocktime > DateUtils.dateStringToTimestamp(versionTime)) {
|
|
294
|
+
return response;
|
|
295
|
+
}
|
|
286
296
|
}
|
|
287
297
|
|
|
288
298
|
// Check update.targetVersionId against currentVersionId
|
|
@@ -294,23 +304,21 @@ export class Resolve {
|
|
|
294
304
|
|
|
295
305
|
// If update.targetVersionId == currentVersionId + 1, apply the update
|
|
296
306
|
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
307
|
// Check if update.sourceHash !== currentDocumentHash
|
|
301
|
-
if (sourceHash !== currentDocumentHash) {
|
|
308
|
+
if (update.sourceHash !== currentDocumentHash) {
|
|
302
309
|
// Raise an INVALID_DID_UPDATE error if they do not match
|
|
303
310
|
throw new ResolveError(
|
|
304
311
|
`Hash mismatch: update.sourceHash !== currentDocumentHash`,
|
|
305
|
-
INVALID_DID_UPDATE, { sourceHash, currentDocumentHash }
|
|
312
|
+
INVALID_DID_UPDATE, { sourceHash: update.sourceHash, currentDocumentHash }
|
|
306
313
|
);
|
|
307
314
|
}
|
|
308
315
|
// Apply the update to the currentDocument and set it in the response
|
|
309
|
-
response.
|
|
316
|
+
response.didDocument = await this.applyUpdate(response.didDocument, update);
|
|
317
|
+
|
|
310
318
|
// Create unsigned_update by removing the proof property from update.
|
|
311
319
|
const unsignedUpdate = JSONUtils.deleteKeys(update, ['proof']) as UnsignedBTCR2Update;
|
|
312
320
|
// Push the canonicalized unsigned update hash to the updateHashHistory
|
|
313
|
-
updateHashHistory.push(
|
|
321
|
+
updateHashHistory.push(Canonicalization.process(unsignedUpdate, { encoding: 'base58' }));
|
|
314
322
|
}
|
|
315
323
|
|
|
316
324
|
// If update.targetVersionId > currentVersionId + 1, throw LATE_PUBLISHING error
|
|
@@ -319,15 +327,16 @@ export class Resolve {
|
|
|
319
327
|
`Version Id Mismatch: targetVersionId cannot be > currentVersionId + 1`,
|
|
320
328
|
'LATE_PUBLISHING_ERROR', {
|
|
321
329
|
targetVersionId : update.targetVersionId,
|
|
322
|
-
currentVersionId :
|
|
330
|
+
currentVersionId : currentVersionId + 1
|
|
323
331
|
}
|
|
324
332
|
);
|
|
325
333
|
}
|
|
326
334
|
|
|
327
335
|
// Increment currentVersionId
|
|
328
336
|
currentVersionId++;
|
|
329
|
-
|
|
330
|
-
response.versionId
|
|
337
|
+
|
|
338
|
+
// Set response.versionId to be the new currentVersionId
|
|
339
|
+
response.metadata.versionId = `${currentVersionId}`;
|
|
331
340
|
|
|
332
341
|
// If resolutionOptions.versionId is defined and <= currentVersionId, return currentDocument
|
|
333
342
|
if(currentVersionId >= Number(versionId)) {
|
|
@@ -336,6 +345,9 @@ export class Resolve {
|
|
|
336
345
|
|
|
337
346
|
// Check if the current document is deactivated before further processing
|
|
338
347
|
if(currentDocument.deactivated) {
|
|
348
|
+
// Set the response deactivated flag to true
|
|
349
|
+
response.metadata.deactivated = currentDocument.deactivated;
|
|
350
|
+
// If deactivated, stop processing further updates and return the response
|
|
339
351
|
return response;
|
|
340
352
|
}
|
|
341
353
|
}
|
|
@@ -352,12 +364,12 @@ export class Resolve {
|
|
|
352
364
|
*/
|
|
353
365
|
static confirmDuplicate(update: SignedBTCR2Update, updateHashHistory: string[]): void {
|
|
354
366
|
// Create unsigned_update by removing the proof property from update.
|
|
355
|
-
const
|
|
367
|
+
const { proof: _, ...unsignedUpdate } = update;
|
|
356
368
|
|
|
357
369
|
// Hash unsignedUpdate with JSON Document Hashing algorithm
|
|
358
|
-
const unsignedUpdateHash =
|
|
370
|
+
const unsignedUpdateHash = Canonicalization.process(unsignedUpdate);
|
|
359
371
|
|
|
360
|
-
//
|
|
372
|
+
// Let historicalUpdateHash equal updateHashHistory[updateHashIndex].
|
|
361
373
|
const historicalUpdateHash = updateHashHistory[update.targetVersionId - 2];
|
|
362
374
|
|
|
363
375
|
// Check if the updateHash matches the historical hash
|
|
@@ -420,7 +432,7 @@ export class Resolve {
|
|
|
420
432
|
const cryptosuite = new BIP340Cryptosuite(multikey);
|
|
421
433
|
|
|
422
434
|
// Canonicalize the update
|
|
423
|
-
const canonicalUpdate =
|
|
435
|
+
const canonicalUpdate = Canonicalization.canonicalize(update);
|
|
424
436
|
|
|
425
437
|
// Construct a DataIntegrityProof with the cryptosuite
|
|
426
438
|
const diProof = new BIP340DataIntegrityProof(cryptosuite);
|
|
@@ -443,7 +455,7 @@ export class Resolve {
|
|
|
443
455
|
DidDocument.validate(updatedDocument);
|
|
444
456
|
|
|
445
457
|
// Canonicalize and hash the updatedDocument to get the currentDocumentHash.
|
|
446
|
-
const currentDocumentHash =
|
|
458
|
+
const currentDocumentHash = Canonicalization.process(updatedDocument, { encoding: 'base58' });
|
|
447
459
|
|
|
448
460
|
// Prepare the update targetHash for comparison with currentDocumentHash.
|
|
449
461
|
const updateTargetHash = update.targetHash.startsWith('z') ? update.targetHash : `z${update.targetHash}`;
|
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,7 +12,6 @@ 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';
|
|
@@ -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: 'base58' }),
|
|
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: 'base58' });
|
|
78
78
|
|
|
79
79
|
// Return unsignedUpdate.
|
|
80
80
|
return unsignedUpdate;
|
package/src/did-btcr2.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { BitcoinNetworkConnection } from '@did-btcr2/bitcoin';
|
|
2
2
|
import {
|
|
3
|
-
Canonicalization,
|
|
4
3
|
DocumentBytes,
|
|
5
4
|
HexString,
|
|
6
5
|
IdentifierHrp,
|
|
@@ -34,9 +33,6 @@ import { Update } from './core/update.js';
|
|
|
34
33
|
import { Appendix } from './utils/appendix.js';
|
|
35
34
|
import { Btcr2DidDocument, DidVerificationMethod } from './utils/did-document.js';
|
|
36
35
|
|
|
37
|
-
// TODO: convert to API driver?
|
|
38
|
-
export const canonicalization = new Canonicalization();
|
|
39
|
-
|
|
40
36
|
export type Btcr2Identifier = string;
|
|
41
37
|
|
|
42
38
|
export interface DidCreateOptions {
|
|
@@ -81,11 +77,16 @@ export class DidBtcr2 implements DidMethod {
|
|
|
81
77
|
* @param {string} options.network The Bitcoin network to use for the identifier, e.g. 'bitcoin', 'testnet', etc. Defaults to 'bitcoin'.
|
|
82
78
|
* @returns {Promise<Btcr2Identifier>} Promise resolving to a Btcr2Identifier string.
|
|
83
79
|
* @throws {MethodError} if any of the checks fail
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* const genesisBytes = SchnorrKeyPair.generate().publicKey.compressed;
|
|
83
|
+
* const did = DidBtcr2.create(genesisBytes, { idType: 'KEY', network: 'regtest' });
|
|
84
|
+
* ```
|
|
84
85
|
*/
|
|
85
|
-
static
|
|
86
|
+
static create(
|
|
86
87
|
genesisBytes: KeyBytes | DocumentBytes,
|
|
87
88
|
options?: DidCreateOptions
|
|
88
|
-
):
|
|
89
|
+
): Btcr2Identifier {
|
|
89
90
|
// Deconstruct the idType, version and network from the options, setting defaults if not given
|
|
90
91
|
const { idType, version = 1, network = 'bitcoin' } = options || {};
|
|
91
92
|
|
|
@@ -117,24 +118,26 @@ export class DidBtcr2 implements DidMethod {
|
|
|
117
118
|
* @example
|
|
118
119
|
* ```ts
|
|
119
120
|
* const resolution = await DidBtcr2.resolve(
|
|
120
|
-
* 'did:btcr2:
|
|
121
|
+
* 'did:btcr2:k1qgpr45cheptyjekl3cex80xfnkwhxnlclecwwf92gvdjrszm2uwhzlcxu5xte'
|
|
121
122
|
* )
|
|
122
123
|
* ```
|
|
123
124
|
*/
|
|
124
125
|
static async resolve(
|
|
125
126
|
did: string,
|
|
126
|
-
resolutionOptions: ResolutionOptions = {
|
|
127
|
+
resolutionOptions: ResolutionOptions = {}
|
|
127
128
|
): Promise<DidResolutionResult> {
|
|
128
129
|
try {
|
|
130
|
+
// Set versionId from resolutionOptions
|
|
131
|
+
const versionId = resolutionOptions.versionId;
|
|
129
132
|
|
|
130
133
|
// Initialize an empty DID Resolution Result
|
|
131
134
|
const didResolutionResult: DidResolutionResult = {
|
|
132
135
|
'@context' : 'https://w3id.org/did-resolution/v1',
|
|
133
136
|
didResolutionMetadata : { contentType: 'application/ld+json' },
|
|
134
137
|
didDocumentMetadata : {
|
|
138
|
+
versionId,
|
|
135
139
|
deactivated : false,
|
|
136
140
|
updated : undefined,
|
|
137
|
-
versionId : resolutionOptions.versionId,
|
|
138
141
|
confirmations : undefined,
|
|
139
142
|
},
|
|
140
143
|
didDocument : null,
|
|
@@ -146,25 +149,16 @@ export class DidBtcr2 implements DidMethod {
|
|
|
146
149
|
// Process sidecar if provided
|
|
147
150
|
const sidecarData = Resolve.sidecarData(resolutionOptions.sidecar);
|
|
148
151
|
|
|
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
152
|
// Parse the genesis document from the resolution options if provided
|
|
160
153
|
const genesisDocument = resolutionOptions.sidecar?.genesisDocument;
|
|
154
|
+
|
|
161
155
|
// Since genesisDocument is optional, check if it exists
|
|
162
156
|
if(!genesisDocument) {
|
|
163
157
|
// If no genesisDocument and x HRP, throw MISSING_UPDATE_DATA error
|
|
164
158
|
if(didComponents.hrp === IdentifierHrp.x)
|
|
165
159
|
throw new ResolveError(
|
|
166
160
|
'External resolution requires genesisDocument',
|
|
167
|
-
MISSING_UPDATE_DATA,
|
|
161
|
+
MISSING_UPDATE_DATA, resolutionOptions
|
|
168
162
|
);
|
|
169
163
|
}
|
|
170
164
|
|
|
@@ -176,23 +170,33 @@ export class DidBtcr2 implements DidMethod {
|
|
|
176
170
|
.filter(BeaconUtils.isBeaconService)
|
|
177
171
|
.map(BeaconUtils.parseBeaconServiceEndpoint);
|
|
178
172
|
|
|
179
|
-
|
|
173
|
+
// Check if bitcoin driver provided
|
|
174
|
+
if(!resolutionOptions?.drivers?.bitcoin) {
|
|
175
|
+
// If not, initialize default drivers
|
|
176
|
+
resolutionOptions.drivers = resolutionOptions.drivers || {};
|
|
177
|
+
// Set bitcoin driver to default BitcoinNetworkConnection
|
|
178
|
+
resolutionOptions.drivers.bitcoin = new BitcoinNetworkConnection();
|
|
179
|
+
// Set the network based on the decoded identifier
|
|
180
|
+
resolutionOptions.drivers.bitcoin.setActiveNetwork(didComponents.network);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Get the bitcoin driver from the resolution options
|
|
184
|
+
const bitcoin = resolutionOptions.drivers.bitcoin;
|
|
180
185
|
|
|
181
186
|
// Process the Beacon Signals to get the required updates
|
|
182
187
|
const unsortedUpdates = await Resolve.beaconSignals(
|
|
183
188
|
beaconServices,
|
|
184
189
|
sidecarData,
|
|
185
|
-
|
|
190
|
+
bitcoin
|
|
186
191
|
);
|
|
187
|
-
console.log('unsortedUpdates', unsortedUpdates);
|
|
188
192
|
|
|
189
193
|
// If no updates found, return the current document
|
|
190
194
|
if(!unsortedUpdates.length) {
|
|
191
|
-
// Set the
|
|
195
|
+
// Set the didDocument in didResolutionResult based on currentDocument
|
|
192
196
|
didResolutionResult.didDocument = currentDocument;
|
|
193
|
-
|
|
194
|
-
// Set the deactivated status in the didDocumentMetadata
|
|
197
|
+
// Set other required fields in the didResolutionResult
|
|
195
198
|
didResolutionResult.didDocumentMetadata.deactivated = !!currentDocument.deactivated;
|
|
199
|
+
didResolutionResult.didDocumentMetadata.versionId = versionId ?? '1';
|
|
196
200
|
|
|
197
201
|
// Return the didResolutionResult early
|
|
198
202
|
return didResolutionResult;
|
|
@@ -203,14 +207,12 @@ export class DidBtcr2 implements DidMethod {
|
|
|
203
207
|
currentDocument,
|
|
204
208
|
unsortedUpdates,
|
|
205
209
|
resolutionOptions.versionTime,
|
|
206
|
-
|
|
210
|
+
versionId
|
|
207
211
|
);
|
|
208
212
|
|
|
209
213
|
// 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;
|
|
214
|
+
didResolutionResult.didDocument = result.didDocument;
|
|
215
|
+
didResolutionResult.didDocumentMetadata = result.metadata;
|
|
214
216
|
|
|
215
217
|
// Return didResolutionResult;
|
|
216
218
|
return didResolutionResult;
|
|
@@ -339,7 +341,7 @@ export class DidBtcr2 implements DidMethod {
|
|
|
339
341
|
bitcoin ??= new BitcoinNetworkConnection();
|
|
340
342
|
|
|
341
343
|
// Announce the signed update to the blockchain using the specified beacon(s)
|
|
342
|
-
|
|
344
|
+
await Update.announce(beaconService, signed, secretKey, bitcoin);
|
|
343
345
|
|
|
344
346
|
// Return signed update if announced successfully
|
|
345
347
|
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;
|