@did-btcr2/method 0.23.0 → 0.25.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.
Files changed (160) hide show
  1. package/README.md +96 -50
  2. package/dist/browser.js +36332 -37280
  3. package/dist/browser.mjs +36331 -37279
  4. package/dist/cjs/core/beacon/aggregation/communication/adapter/did-comm.js +1 -1
  5. package/dist/cjs/core/beacon/aggregation/communication/adapter/did-comm.js.map +1 -1
  6. package/dist/cjs/core/beacon/aggregation/communication/adapter/nostr.js +1 -1
  7. package/dist/cjs/core/beacon/aggregation/communication/adapter/nostr.js.map +1 -1
  8. package/dist/cjs/core/beacon/aggregation/coordinator.js +40 -44
  9. package/dist/cjs/core/beacon/aggregation/coordinator.js.map +1 -1
  10. package/dist/cjs/core/beacon/aggregation/participant.js +35 -38
  11. package/dist/cjs/core/beacon/aggregation/participant.js.map +1 -1
  12. package/dist/cjs/core/beacon/aggregation/session/index.js +3 -4
  13. package/dist/cjs/core/beacon/aggregation/session/index.js.map +1 -1
  14. package/dist/cjs/core/beacon/beacon.js.map +1 -1
  15. package/dist/cjs/core/beacon/cas-beacon.js +119 -7
  16. package/dist/cjs/core/beacon/cas-beacon.js.map +1 -1
  17. package/dist/cjs/core/beacon/factory.js +1 -1
  18. package/dist/cjs/core/beacon/factory.js.map +1 -1
  19. package/dist/cjs/core/beacon/{singleton.js → singleton-beacon.js} +19 -27
  20. package/dist/cjs/core/beacon/singleton-beacon.js.map +1 -0
  21. package/dist/cjs/core/beacon/smt-beacon.js +1 -1
  22. package/dist/cjs/core/beacon/smt-beacon.js.map +1 -1
  23. package/dist/cjs/core/identifier.js +1 -1
  24. package/dist/cjs/core/identifier.js.map +1 -1
  25. package/dist/{esm/core/resolve.js → cjs/core/resolver.js} +244 -92
  26. package/dist/cjs/core/resolver.js.map +1 -0
  27. package/dist/cjs/core/update.js +7 -7
  28. package/dist/cjs/core/update.js.map +1 -1
  29. package/dist/cjs/did-btcr2.js +34 -94
  30. package/dist/cjs/did-btcr2.js.map +1 -1
  31. package/dist/cjs/index.js +2 -3
  32. package/dist/cjs/index.js.map +1 -1
  33. package/dist/cjs/utils/did-document.js +9 -19
  34. package/dist/cjs/utils/did-document.js.map +1 -1
  35. package/dist/esm/core/beacon/aggregation/communication/adapter/did-comm.js +1 -1
  36. package/dist/esm/core/beacon/aggregation/communication/adapter/did-comm.js.map +1 -1
  37. package/dist/esm/core/beacon/aggregation/communication/adapter/nostr.js +1 -1
  38. package/dist/esm/core/beacon/aggregation/communication/adapter/nostr.js.map +1 -1
  39. package/dist/esm/core/beacon/aggregation/coordinator.js +40 -44
  40. package/dist/esm/core/beacon/aggregation/coordinator.js.map +1 -1
  41. package/dist/esm/core/beacon/aggregation/participant.js +35 -38
  42. package/dist/esm/core/beacon/aggregation/participant.js.map +1 -1
  43. package/dist/esm/core/beacon/aggregation/session/index.js +3 -4
  44. package/dist/esm/core/beacon/aggregation/session/index.js.map +1 -1
  45. package/dist/esm/core/beacon/beacon.js.map +1 -1
  46. package/dist/esm/core/beacon/cas-beacon.js +119 -7
  47. package/dist/esm/core/beacon/cas-beacon.js.map +1 -1
  48. package/dist/esm/core/beacon/factory.js +1 -1
  49. package/dist/esm/core/beacon/factory.js.map +1 -1
  50. package/dist/esm/core/beacon/{singleton.js → singleton-beacon.js} +19 -27
  51. package/dist/esm/core/beacon/singleton-beacon.js.map +1 -0
  52. package/dist/esm/core/beacon/smt-beacon.js +1 -1
  53. package/dist/esm/core/beacon/smt-beacon.js.map +1 -1
  54. package/dist/esm/core/identifier.js +1 -1
  55. package/dist/esm/core/identifier.js.map +1 -1
  56. package/dist/{cjs/core/resolve.js → esm/core/resolver.js} +244 -92
  57. package/dist/esm/core/resolver.js.map +1 -0
  58. package/dist/esm/core/update.js +7 -7
  59. package/dist/esm/core/update.js.map +1 -1
  60. package/dist/esm/did-btcr2.js +34 -94
  61. package/dist/esm/did-btcr2.js.map +1 -1
  62. package/dist/esm/index.js +2 -3
  63. package/dist/esm/index.js.map +1 -1
  64. package/dist/esm/utils/did-document.js +9 -19
  65. package/dist/esm/utils/did-document.js.map +1 -1
  66. package/dist/types/core/beacon/aggregation/cohort/index.d.ts +1 -0
  67. package/dist/types/core/beacon/aggregation/cohort/messages/base.d.ts +1 -0
  68. package/dist/types/core/beacon/aggregation/cohort/messages/constants.d.ts +1 -0
  69. package/dist/types/core/beacon/aggregation/cohort/messages/index.d.ts +1 -0
  70. package/dist/types/core/beacon/aggregation/cohort/messages/keygen/cohort-advert.d.ts +1 -0
  71. package/dist/types/core/beacon/aggregation/cohort/messages/keygen/cohort-ready.d.ts +2 -2
  72. package/dist/types/core/beacon/aggregation/cohort/messages/keygen/cohort-ready.d.ts.map +1 -1
  73. package/dist/types/core/beacon/aggregation/cohort/messages/keygen/opt-in-accept.d.ts +1 -0
  74. package/dist/types/core/beacon/aggregation/cohort/messages/keygen/opt-in.d.ts +1 -0
  75. package/dist/types/core/beacon/aggregation/cohort/messages/keygen/subscribe.d.ts +1 -0
  76. package/dist/types/core/beacon/aggregation/cohort/messages/sign/aggregated-nonce.d.ts +1 -0
  77. package/dist/types/core/beacon/aggregation/cohort/messages/sign/authorization-request.d.ts +1 -0
  78. package/dist/types/core/beacon/aggregation/cohort/messages/sign/nonce-contribution.d.ts +1 -0
  79. package/dist/types/core/beacon/aggregation/cohort/messages/sign/request-signature.d.ts +1 -0
  80. package/dist/types/core/beacon/aggregation/cohort/messages/sign/signature-authorization.d.ts +1 -0
  81. package/dist/types/core/beacon/aggregation/cohort/status.d.ts +1 -0
  82. package/dist/types/core/beacon/aggregation/communication/adapter/did-comm.d.ts +4 -3
  83. package/dist/types/core/beacon/aggregation/communication/adapter/did-comm.d.ts.map +1 -1
  84. package/dist/types/core/beacon/aggregation/communication/adapter/nostr.d.ts +5 -3
  85. package/dist/types/core/beacon/aggregation/communication/adapter/nostr.d.ts.map +1 -1
  86. package/dist/types/core/beacon/aggregation/communication/error.d.ts +1 -0
  87. package/dist/types/core/beacon/aggregation/communication/factory.d.ts +1 -0
  88. package/dist/types/core/beacon/aggregation/communication/service.d.ts +3 -2
  89. package/dist/types/core/beacon/aggregation/communication/service.d.ts.map +1 -1
  90. package/dist/types/core/beacon/aggregation/coordinator.d.ts +1 -0
  91. package/dist/types/core/beacon/aggregation/coordinator.d.ts.map +1 -1
  92. package/dist/types/core/beacon/aggregation/participant.d.ts +1 -0
  93. package/dist/types/core/beacon/aggregation/participant.d.ts.map +1 -1
  94. package/dist/types/core/beacon/aggregation/session/index.d.ts +1 -0
  95. package/dist/types/core/beacon/aggregation/session/index.d.ts.map +1 -1
  96. package/dist/types/core/beacon/aggregation/session/status.d.ts +1 -0
  97. package/dist/types/core/beacon/beacon.d.ts +10 -4
  98. package/dist/types/core/beacon/beacon.d.ts.map +1 -1
  99. package/dist/types/core/beacon/cas-beacon.d.ts +27 -7
  100. package/dist/types/core/beacon/cas-beacon.d.ts.map +1 -1
  101. package/dist/types/core/beacon/error.d.ts +1 -0
  102. package/dist/types/core/beacon/factory.d.ts +1 -0
  103. package/dist/types/core/beacon/interfaces.d.ts +1 -0
  104. package/dist/types/core/beacon/signal-discovery.d.ts +1 -0
  105. package/dist/types/core/beacon/{singleton.d.ts → singleton-beacon.d.ts} +7 -5
  106. package/dist/types/core/beacon/singleton-beacon.d.ts.map +1 -0
  107. package/dist/types/core/beacon/smt-beacon.d.ts +5 -3
  108. package/dist/types/core/beacon/smt-beacon.d.ts.map +1 -1
  109. package/dist/types/core/beacon/utils.d.ts +1 -0
  110. package/dist/types/core/identifier.d.ts +1 -0
  111. package/dist/types/core/interfaces.d.ts +6 -15
  112. package/dist/types/core/interfaces.d.ts.map +1 -1
  113. package/dist/types/core/resolver.d.ts +167 -0
  114. package/dist/types/core/resolver.d.ts.map +1 -0
  115. package/dist/types/core/types.d.ts +1 -0
  116. package/dist/types/core/update.d.ts +4 -3
  117. package/dist/types/core/update.d.ts.map +1 -1
  118. package/dist/types/did-btcr2.d.ts +17 -16
  119. package/dist/types/did-btcr2.d.ts.map +1 -1
  120. package/dist/types/index.d.ts +3 -3
  121. package/dist/types/index.d.ts.map +1 -1
  122. package/dist/types/utils/appendix.d.ts +1 -0
  123. package/dist/types/utils/did-document-builder.d.ts +1 -0
  124. package/dist/types/utils/did-document.d.ts +2 -6
  125. package/dist/types/utils/did-document.d.ts.map +1 -1
  126. package/package.json +5 -5
  127. package/src/core/beacon/aggregation/cohort/messages/keygen/cohort-ready.ts +1 -1
  128. package/src/core/beacon/aggregation/communication/adapter/did-comm.ts +4 -3
  129. package/src/core/beacon/aggregation/communication/adapter/nostr.ts +4 -3
  130. package/src/core/beacon/aggregation/communication/service.ts +2 -2
  131. package/src/core/beacon/aggregation/coordinator.ts +40 -44
  132. package/src/core/beacon/aggregation/participant.ts +38 -40
  133. package/src/core/beacon/aggregation/session/index.ts +3 -4
  134. package/src/core/beacon/beacon.ts +9 -5
  135. package/src/core/beacon/cas-beacon.ts +156 -10
  136. package/src/core/beacon/factory.ts +1 -1
  137. package/src/core/beacon/{singleton.ts → singleton-beacon.ts} +20 -36
  138. package/src/core/beacon/smt-beacon.ts +4 -3
  139. package/src/core/identifier.ts +1 -1
  140. package/src/core/interfaces.ts +5 -16
  141. package/src/core/resolver.ts +706 -0
  142. package/src/core/update.ts +9 -9
  143. package/src/did-btcr2.ts +37 -130
  144. package/src/index.ts +2 -3
  145. package/src/utils/did-document.ts +10 -18
  146. package/dist/cjs/core/beacon/singleton.js.map +0 -1
  147. package/dist/cjs/core/resolve.js.map +0 -1
  148. package/dist/cjs/utils/general.js +0 -195
  149. package/dist/cjs/utils/general.js.map +0 -1
  150. package/dist/esm/core/beacon/singleton.js.map +0 -1
  151. package/dist/esm/core/resolve.js.map +0 -1
  152. package/dist/esm/utils/general.js +0 -195
  153. package/dist/esm/utils/general.js.map +0 -1
  154. package/dist/types/core/beacon/singleton.d.ts.map +0 -1
  155. package/dist/types/core/resolve.d.ts +0 -92
  156. package/dist/types/core/resolve.d.ts.map +0 -1
  157. package/dist/types/utils/general.d.ts +0 -85
  158. package/dist/types/utils/general.d.ts.map +0 -1
  159. package/src/core/resolve.ts +0 -474
  160. package/src/utils/general.ts +0 -204
@@ -1,474 +0,0 @@
1
- import {
2
- BitcoinConnection,
3
- getNetwork
4
- } from '@did-btcr2/bitcoin';
5
- import {
6
- Canonicalization,
7
- DateUtils,
8
- IdentifierHrp,
9
- INVALID_DID,
10
- INVALID_DID_DOCUMENT,
11
- INVALID_DID_UPDATE,
12
- JSONPatch,
13
- JSONUtils,
14
- LATE_PUBLISHING_ERROR,
15
- MISSING_UPDATE_DATA,
16
- ResolveError
17
- } from '@did-btcr2/common';
18
- import {
19
- BIP340Cryptosuite,
20
- BIP340DataIntegrityProof,
21
- SchnorrMultikey,
22
- SignedBTCR2Update,
23
- UnsignedBTCR2Update
24
- } from '@did-btcr2/cryptosuite';
25
- import { CompressedSecp256k1PublicKey } from '@did-btcr2/keypair';
26
- import { bytesToHex } from '@noble/hashes/utils';
27
- import { DidBtcr2 } from '../did-btcr2.js';
28
- import { Appendix } from '../utils/appendix.js';
29
- import { DidDocument, ID_PLACEHOLDER_VALUE } from '../utils/did-document.js';
30
- import { BeaconFactory } from './beacon/factory.js';
31
- import { BeaconService, BlockMetadata } from './beacon/interfaces.js';
32
- import { BeaconSignalDiscovery } from './beacon/signal-discovery.js';
33
- import { BeaconUtils } from './beacon/utils.js';
34
- import { DidComponents, Identifier } from './identifier.js';
35
- import { SMTProof } from './interfaces.js';
36
- import { CASAnnouncement, Sidecar, SidecarData } from './types.js';
37
-
38
- /**
39
- * The response object for DID Resolution.
40
- */
41
- export interface DidResolutionResponse {
42
- didDocument: DidDocument;
43
- metadata: {
44
- confirmations?: number;
45
- versionId: string;
46
- updated?: string;
47
- deactivated?: boolean;
48
- }
49
- }
50
-
51
- /**
52
- * Implements {@link https://dcdpr.github.io/did-btcr2/operations/resolve.html | 7.2 Resolve}.
53
- * Resolving a did:btcr2 identifier iteratively builds a DID document by applying BTCR2 Updates
54
- * to an Initial DID Document that have been committed to the Bitcoin blockchain by Authorized
55
- * Beacon Signals. The Initial DID Document is either deterministically created from the DID or
56
- * provided by Sidecar Data.
57
- * @class Resolve
58
- * @type {Resolve}
59
- */
60
- export class Resolve {
61
- /**
62
- * Implements subsection {@link https://dcdpr.github.io/did-btcr2/operations/resolve.html#if-genesis_bytes-is-a-secp256k1-public-key | 7.2.d.1 if genesis bytes is a secp256k1 Public Key}.
63
- * @param {DidComponents} didComponents The decoded components of the did.
64
- * @returns {DidDocument} The resolved DID Document object.
65
- */
66
- static deterministic(didComponents: DidComponents): DidDocument {
67
- // Deconstruct the bytes from the given components
68
- const { genesisBytes } = didComponents;
69
-
70
- // Encode the did from the didComponents
71
- const did = Identifier.encode(genesisBytes, didComponents);
72
-
73
- // Construct a new CompressedSecp256k1PublicKey and deconstruct the publicKey and publicKeyMultibase
74
- const { multibase: publicKeyMultibase } = new CompressedSecp256k1PublicKey(genesisBytes);
75
-
76
- // Generate the service field for the DID Document
77
- const service = BeaconUtils.generateBeaconServices({
78
- id : did,
79
- publicKey : genesisBytes,
80
- network : getNetwork(didComponents.network),
81
- beaconType : 'SingletonBeacon'
82
- });
83
-
84
- return new DidDocument({
85
- id : did,
86
- verificationMethod : [{
87
- id : `${did}#initialKey`,
88
- type : 'Multikey',
89
- controller : did,
90
- publicKeyMultibase : publicKeyMultibase.encoded
91
- }],
92
- service
93
- });
94
- }
95
-
96
- /**
97
- * Implements subsection {@link https://dcdpr.github.io/did-btcr2/operations/resolve.html#if-genesis_bytes-is-a-sha-256-hash | 7.2.d.2 if genesis_bytes is a SHA-256 Hash}.
98
- * @param {DidComponents} didComponents BTCR2 DID components used to resolve the DID Document
99
- * @param {GenesisDocument} genesisDocument The genesis document for resolving the DID Document.
100
- * @returns {Promise<DidDocument>} The resolved DID Document object
101
- * @throws {ResolveError} InvalidDidDocument if not conformant to DID Core v1.1
102
- */
103
- static async external(
104
- didComponents: DidComponents,
105
- genesisDocument: object,
106
- ): Promise<DidDocument> {
107
- // Canonicalize and sha256 hash the currentDocument
108
- const hashBytes = Canonicalization.process(genesisDocument, { encoding: 'hex' });
109
-
110
- const { genesisBytes } = didComponents;
111
-
112
- // If the genesisBytes do not match the hashBytes, throw an error
113
- if (bytesToHex(genesisBytes) !== hashBytes) {
114
- throw new ResolveError(
115
- `Initial document mismatch: genesisBytes ${bytesToHex(genesisBytes)} !== hashBytes ${hashBytes}`,
116
- INVALID_DID_DOCUMENT, { genesisBytes, hashBytes }
117
- );
118
- }
119
-
120
- // Encode the did from the didComponents
121
- const did = Identifier.encode(genesisBytes, didComponents);
122
-
123
- // Replace the placeholder did with the did throughout the currentDocument.
124
- const currentDocument = JSON.parse(
125
- JSON.stringify(genesisDocument).replaceAll(ID_PLACEHOLDER_VALUE, did)
126
- );
127
-
128
- // Return a W3C conformant DID Document
129
- return new DidDocument(currentDocument);
130
- }
131
-
132
- /**
133
- * Implements subsection {@link https://dcdpr.github.io/did-btcr2/operations/resolve.html#process-sidecar-data | Process Sidecar Data}
134
- * @param {Sidecar} sidecar The sidecar data to process.
135
- * @returns {ProcessedSidecar} The processed sidecar data containing maps of updates, CAS announcements, and SMT proofs.
136
- */
137
- static sidecarData(sidecar: Sidecar = {} as Sidecar): SidecarData {
138
- // BTCR2 Signed Updates map
139
- const updateMap = new Map<string, SignedBTCR2Update>();
140
- if(sidecar.updates?.length)
141
- for(const update of sidecar.updates) {
142
- updateMap.set(Canonicalization.process(update, { encoding: 'hex' }), update);
143
- }
144
-
145
- // CAS Announcements map
146
- const casMap = new Map<string, CASAnnouncement>();
147
- if(sidecar.casUpdates?.length)
148
- for(const update of sidecar.casUpdates) {
149
- casMap.set(Canonicalization.process(update, { encoding: 'hex' }), update);
150
- }
151
-
152
- // SMT Proofs map
153
- const smtMap = new Map<string, SMTProof>();
154
- if(sidecar.smtProofs?.length)
155
- for(const proof of sidecar.smtProofs) {
156
- smtMap.set(proof.id, proof);
157
- }
158
-
159
- return { updateMap, casMap, smtMap };
160
- }
161
-
162
- /**
163
- * Implements subsection {@link https://dcdpr.github.io/did-btcr2/operations/resolve.html#establish-current-document | 7.2.d Establish current_document}.
164
- * Resolution begins by creating an Initial Did Document called current_document (Current DID Document).
165
- * The current_document is iteratively patched with BTCR2 Signed Updates announced by Authorized Beacon Signals.
166
- * @param {DidComponents} didComponents The decoded components of the did.
167
- * @param {GenesisDocument} genesisDocument The genesis document for resolving the DID Document.
168
- * @returns {Promise<DidDocument>} The resolved DID Document object.
169
- * @throws {ResolveError} if the DID hrp is invalid, no sidecarData passed and hrp = "x".
170
- */
171
-
172
- static async currentDocument(
173
- didComponents: DidComponents,
174
- genesisDocument?: object,
175
- ): Promise<DidDocument> {
176
- // Deconstruct the hrp from the components
177
- const { hrp, genesisBytes } = didComponents;
178
-
179
- // If hrp `x`, perform external resolution
180
- if (hrp === IdentifierHrp.x) {
181
- if(!genesisDocument)
182
- throw new ResolveError(
183
- 'External resolution requires genesisDocument',
184
- MISSING_UPDATE_DATA, { didComponents }
185
- );
186
- return await this.external(didComponents, genesisDocument);
187
- }
188
-
189
- // Check for hrp `k`
190
- if(hrp === IdentifierHrp.k){
191
- // Validate genesis bytes as a compressed secp256k1 public key
192
- if(!CompressedSecp256k1PublicKey.isValid(genesisBytes)) {
193
- throw new ResolveError(
194
- 'Deterministic resolution requires valid secp256k1 public key',
195
- INVALID_DID, { genesisBytes }
196
- );
197
- }
198
- // Perform deterministic resolution
199
- return this.deterministic(didComponents);
200
- }
201
-
202
- // Else, throw an error for unsupported hrp
203
- throw new ResolveError(`Unsupported DID hrp ${hrp}`, INVALID_DID, { hrp });
204
- }
205
-
206
- /**
207
- * Finds uses the beacon services in the currentDocument to scan for onchain Beacon Signals (transactions) containing
208
- * Signal Bytes (last output in OP_RETURN transaction).
209
- * @param {Array<BeaconService>} beaconServices The array of BeaconService objects to search for signals.
210
- * @param {SidecarData} sidecarData The sidecar data containing maps of updates, CAS announcements, and SMT proofs.
211
- * @param {BitcoinConnection} bitcoin The bitcoin network connection used to fetch beacon signals
212
- * @returns {Promise<Array<[SignedBTCR2Update, BlockMetadata]>>} The array of BTCR2 Signed Updates announced by the Beacon Signals.
213
- */
214
- static async beaconSignals(
215
- beaconServices: Array<BeaconService>,
216
- sidecarData: SidecarData,
217
- bitcoin: BitcoinConnection
218
- ): Promise<Array<[SignedBTCR2Update, BlockMetadata]>> {
219
- // Discover Beacon Signals via indexer server (esplora/electrs) or full node
220
- const beaconServicesSignals = bitcoin.rest
221
- ? await BeaconSignalDiscovery.indexer(beaconServices, bitcoin)
222
- : await BeaconSignalDiscovery.fullnode(beaconServices, bitcoin);
223
-
224
- // Process each beacon's signals in parallel
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
- )
234
- );
235
-
236
- return promises.flat();
237
- }
238
-
239
- /**
240
- * Implements subsection {@link https://dcdpr.github.io/did-btcr2/operations/resolve.html#process-updates | 7.2.f Process updates Array}.
241
- * @param {DidDocument} currentDocument The current DID Document to apply the updates to.
242
- * @param {Array<[SignedBTCR2Update, BlockMetadata]>} unsortedUpdates The unsorted array of BTCR2 Signed Updates and their associated Block Metadata.
243
- * @param {string} [versionTime] The optional version time to limit updates to.
244
- * @param {string} [versionId] The optional version id to limit updates to.
245
- * @returns {Promise<DidResolutionResponse>} The updated DID Document, number of confirmations, and version id.
246
- */
247
- static async updates(
248
- currentDocument: DidDocument,
249
- unsortedUpdates: Array<[SignedBTCR2Update, BlockMetadata]>,
250
- versionTime?: string,
251
- versionId?: string
252
- ): Promise<DidResolutionResponse> {
253
- // Start the version number being processed at 1
254
- let currentVersionId = 1;
255
-
256
- // Initialize an empty array to hold the update hashes
257
- const updateHashHistory: string[] = [];
258
-
259
- // 1. Sort updates by targetVersionId (ascending), using blockheight as tie-breaker
260
- const updates = unsortedUpdates.sort(([upd0, blk0], [upd1, blk1]) =>
261
- upd0.targetVersionId - upd1.targetVersionId || blk0.height - blk1.height
262
- );
263
-
264
- // Create a default response object
265
- const response: DidResolutionResponse = {
266
- didDocument : currentDocument,
267
- metadata : {
268
- versionId : `${currentVersionId}`,
269
- confirmations : 0,
270
- updated : '',
271
- deactivated : currentDocument.deactivated || false
272
- }
273
- };
274
-
275
- // Iterate over each (update block) pair
276
- for(const [update, block] of updates) {
277
- // Get the hash of the current document
278
- const currentDocumentHash = Canonicalization.process(response.didDocument, { encoding: 'base58btc' });
279
-
280
- // Safely convert block.time to timestamp
281
- const blocktime = DateUtils.blocktimeToTimestamp(block.time);
282
-
283
- // Set the updated field to the blocktime of the current update
284
- response.metadata.updated = DateUtils.toISOStringNonFractional(blocktime);
285
-
286
- // Set confirmations to the block confirmations
287
- response.metadata.confirmations = block.confirmations;
288
-
289
- // if resolutionOptions.versionTime is defined and the blocktime is more recent, return currentDocument
290
- if(versionTime) {
291
- // Safely convert versionTime to timestamp
292
- if(blocktime > DateUtils.dateStringToTimestamp(versionTime)) {
293
- return response;
294
- }
295
- }
296
-
297
- // Check update.targetVersionId against currentVersionId
298
- // If update.targetVersionId <= currentVersionId, confirm duplicate update
299
- if(update.targetVersionId <= currentVersionId) {
300
- updateHashHistory.push(currentDocumentHash);
301
- this.confirmDuplicate(update, updateHashHistory);
302
- }
303
-
304
- // If update.targetVersionId == currentVersionId + 1, apply the update
305
- else if (update.targetVersionId === currentVersionId + 1) {
306
- // Check if update.sourceHash !== currentDocumentHash
307
- if (update.sourceHash !== currentDocumentHash) {
308
- // Raise an INVALID_DID_UPDATE error if they do not match
309
- throw new ResolveError(
310
- `Hash mismatch: update.sourceHash !== currentDocumentHash`,
311
- INVALID_DID_UPDATE, { sourceHash: update.sourceHash, currentDocumentHash }
312
- );
313
- }
314
- // Apply the update to the currentDocument and set it in the response
315
- response.didDocument = await this.applyUpdate(response.didDocument, update);
316
-
317
- // Create unsigned_update by removing the proof property from update.
318
- const unsignedUpdate = JSONUtils.deleteKeys(update, ['proof']) as UnsignedBTCR2Update;
319
- // Push the canonicalized unsigned update hash to the updateHashHistory
320
- updateHashHistory.push(Canonicalization.process(unsignedUpdate, { encoding: 'base58btc' }));
321
- }
322
-
323
- // If update.targetVersionId > currentVersionId + 1, throw LATE_PUBLISHING error
324
- else if(update.targetVersionId > currentVersionId + 1) {
325
- throw new ResolveError(
326
- `Version Id Mismatch: targetVersionId cannot be > currentVersionId + 1`,
327
- 'LATE_PUBLISHING_ERROR', {
328
- targetVersionId : update.targetVersionId,
329
- currentVersionId : currentVersionId + 1
330
- }
331
- );
332
- }
333
-
334
- // Increment currentVersionId
335
- currentVersionId++;
336
-
337
- // Set response.versionId to be the new currentVersionId
338
- response.metadata.versionId = `${currentVersionId}`;
339
-
340
- // If resolutionOptions.versionId is defined and <= currentVersionId, return currentDocument
341
- if(currentVersionId >= Number(versionId)) {
342
- return response;
343
- }
344
-
345
- // Check if the current document is deactivated before further processing
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
350
- return response;
351
- }
352
- }
353
-
354
- // Return response data
355
- return response;
356
- }
357
-
358
- /**
359
- * Implements subsection {@link https://dcdpr.github.io/did-btcr2/#confirm-duplicate-update | 7.2.f.1 Confirm Duplicate Update}.
360
- * This step confirms that an update with a lower-than-expected targetVersionId is a true duplicate.
361
- * @param {SignedBTCR2Update} update The BTCR2 Signed Update to confirm as a duplicate.
362
- * @returns {void} Does not return a value, but throws an error if the update is not a valid duplicate.
363
- */
364
- static confirmDuplicate(update: SignedBTCR2Update, updateHashHistory: string[]): void {
365
- // Create unsigned_update by removing the proof property from update.
366
- const { proof: _, ...unsignedUpdate } = update;
367
-
368
- // Hash unsignedUpdate with JSON Document Hashing algorithm
369
- const unsignedUpdateHash = Canonicalization.process(unsignedUpdate);
370
-
371
- // Let historicalUpdateHash equal updateHashHistory[updateHashIndex].
372
- const historicalUpdateHash = updateHashHistory[update.targetVersionId - 2];
373
-
374
- // Check if the updateHash matches the historical hash
375
- if (updateHashHistory[update.targetVersionId - 2] !== unsignedUpdateHash) {
376
- throw new ResolveError(
377
- `Invalid duplicate: ${unsignedUpdateHash} does not match ${historicalUpdateHash}`,
378
- LATE_PUBLISHING_ERROR, { unsignedUpdateHash, updateHashHistory }
379
- );
380
- }
381
- }
382
-
383
- /**
384
- * Implements subsection {@link https://dcdpr.github.io/did-btcr2/operations/resolve.html#apply-update | 7.2.f.3 Apply Update}.
385
- * @param {DidDocument} currentDocument The current DID Document to apply the update to.
386
- * @param {SignedBTCR2Update} update The BTCR2 Signed Update to apply.
387
- * @returns {Promise<DidDocument>} The updated DID Document after applying the update.
388
- * @throws {ResolveError} If the update is invalid or cannot be applied.
389
- */
390
- static async applyUpdate(
391
- currentDocument: DidDocument,
392
- update: SignedBTCR2Update
393
- ): Promise<DidDocument> {
394
- // Get the capability id from the to update proof.
395
- const capabilityId = update.proof?.capability;
396
- // Since this field is optional, check that it exists
397
- if (!capabilityId) {
398
- // If it does not exist, throw INVALID_DID_UPDATE error
399
- throw new ResolveError('No root capability found in update', INVALID_DID_UPDATE, update);
400
- }
401
-
402
- // Get the root capability object by dereferencing the capabilityId
403
- const rootCapability = Appendix.dereferenceZcapId(capabilityId);
404
-
405
- // Deconstruct the invocationTarget and controller from the root capability
406
- const { invocationTarget, controller: rootController } = rootCapability;
407
- // Check that both invocationTarget and rootController equal currentDocument.id
408
- if (![invocationTarget, rootController].every((id) => id === currentDocument.id)) {
409
- // If they do not all match, throw INVALID_DID_UPDATE error
410
- throw new ResolveError(
411
- 'Invalid root capability',
412
- INVALID_DID_UPDATE, { rootCapability, currentDocument }
413
- );
414
- }
415
-
416
- // Get the verificationMethod field from the update proof as verificationMethodId.
417
- const verificationMethodId = update.proof?.verificationMethod;
418
- // Since this field is optional, check that it exists
419
- if(!verificationMethodId) {
420
- // If it does not exist, throw INVALID_DID_UPDATE error
421
- throw new ResolveError('No verificationMethod found in update', INVALID_DID_UPDATE, update);
422
- }
423
-
424
- // Get the verificationMethod from the DID Document using the verificationMethodId.
425
- const vm = DidBtcr2.getSigningMethod(currentDocument, verificationMethodId);
426
-
427
- // Construct a new SchnorrMultikey.
428
- const multikey = SchnorrMultikey.fromVerificationMethod(vm);
429
-
430
- // Construct a new BIP340Cryptosuite with the SchnorrMultikey.
431
- const cryptosuite = new BIP340Cryptosuite(multikey);
432
-
433
- // Canonicalize the update
434
- const canonicalUpdate = Canonicalization.canonicalize(update);
435
-
436
- // Construct a DataIntegrityProof with the cryptosuite
437
- const diProof = new BIP340DataIntegrityProof(cryptosuite);
438
-
439
- // Call the verifyProof method
440
- const verificationResult = diProof.verifyProof(canonicalUpdate, 'capabilityInvocation');
441
-
442
- // If the result is not verified, throw INVALID_DID_UPDATE error
443
- if (!verificationResult.verified) {
444
- throw new ResolveError(
445
- 'Invalid update: proof not verified',
446
- INVALID_DID_UPDATE, verificationResult
447
- );
448
- }
449
-
450
- // Apply the update.patch to the currentDocument to get the updatedDocument.
451
- const updatedDocument = JSONPatch.apply(currentDocument, update.patch) as DidDocument;
452
-
453
- // Verify that updatedDocument is conformant to DID Core v1.1.
454
- DidDocument.validate(updatedDocument);
455
-
456
- // Canonicalize and hash the updatedDocument to get the currentDocumentHash.
457
- const currentDocumentHash = Canonicalization.process(updatedDocument, { encoding: 'base58btc' });
458
-
459
- // Prepare the update targetHash for comparison with currentDocumentHash.
460
- const updateTargetHash = update.targetHash;
461
-
462
- // Make sure the update.targetHash equals currentDocumentHash.
463
- if (update.targetHash !== currentDocumentHash) {
464
- // If they do not match, throw INVALID_DID_UPDATE error.
465
- throw new ResolveError(
466
- `Invalid update: update.targetHash !== currentDocumentHash`,
467
- INVALID_DID_UPDATE, { updateTargetHash, currentDocumentHash }
468
- );
469
- }
470
-
471
- // Return final updatedDocument.
472
- return updatedDocument;
473
- }
474
- }
@@ -1,204 +0,0 @@
1
- import { KeyBytes, BIP340_PUBLIC_KEY_MULTIBASE_PREFIX, HdWallet } from '@did-btcr2/common';
2
- import { sha256 } from '@noble/hashes/sha2';
3
- import { CURVE, getPublicKey, utils } from '@noble/secp256k1';
4
- import { HDKey } from '@scure/bip32';
5
- import { generateMnemonic, mnemonicToSeed } from '@scure/bip39';
6
- import { wordlist } from '@scure/bip39/wordlists/english';
7
- import { base58btc } from 'multiformats/bases/base58';
8
-
9
- /**
10
- * Static class of general utility functions for the did-btcr2 spec implementation
11
- * @class GeneralUtils
12
- * @type {GeneralUtils}
13
- */
14
- export class GeneralUtils {
15
- /**
16
- * Helper function to encode a secp256k1 key in SchnorrSecp256k1 Multikey Format
17
- * @param {KeyBytes} xOnlyKeyBytes
18
- * @returns {PublicKeyMultibase}
19
- */
20
- public static encode(xOnlyKeyBytes: KeyBytes): string {
21
- if (xOnlyKeyBytes.length !== 32) {
22
- throw new Error('x-only public key must be 32 bytes');
23
- }
24
- const prefix = Array.from(BIP340_PUBLIC_KEY_MULTIBASE_PREFIX);
25
- const x = Array.from(xOnlyKeyBytes);
26
- // Set the prefix and the public key bytes
27
- const multikeyBytes = new Uint8Array([...prefix, ...x]);
28
- // Encode the public key as a multibase base58btc string
29
- return base58btc.encode(multikeyBytes);
30
- }
31
-
32
- /**
33
- * Converts a bigint to a buffer
34
- * @param {bigint} value The bigint to convert
35
- * @returns {Buffer} The buffer representation of the bigint
36
- */
37
- static bigintToBuffer(value: bigint): Buffer {
38
- const hex = value.toString(16).padStart(64, '0');
39
- return Buffer.from(hex, 'hex');
40
- }
41
-
42
- /**
43
- * Generates a new mnemonic phrase and HD wallet
44
- * @returns {HdWallet} Promise resolving to a new hdwallet object w/ mnemonic and hdkey
45
- * @throws {Error} if the public key bytes cannot be derived
46
- */
47
- static async generateHdWallet(): Promise<HdWallet> {
48
- // Generate random mnemonic phrase.
49
- const mnemonic = generateMnemonic(wordlist, 128);
50
- // Generate seed from random mnemonic phrase.
51
- const seed = await mnemonicToSeed(mnemonic);
52
- // Generate HDKey from seed.
53
- const hdkey = HDKey.fromMasterSeed(seed);
54
- // Ensure HDKey returns valid
55
- if (!hdkey) {
56
- throw new Error('Failed to derive hd wallet');
57
- }
58
- return { mnemonic, hdkey };
59
- }
60
-
61
- static generateCompressedSecp256k1KeyPair(){
62
- const privateKey = utils.randomPrivateKey();
63
- if(!utils.isValidPrivateKey(privateKey)) {
64
- throw new Error('Invalid private key');
65
- }
66
- return { privateKey, publicKey: getPublicKey(privateKey, true) };
67
- };
68
-
69
- /**
70
- * Recovers an HDKey from a mnemonic phrase
71
- * @param {string} mnemonic The mnemonic phrase to recover the HDKey from
72
- * @param {Uint8Array} seed Optional seed to recover the HDKey from
73
- * @returns {HDKey} Promise resolving to the recovered HDKey
74
- * @throws Error if the HDKey cannot be recovered
75
- */
76
- static async recoverHdWallet(mnemonic: string, seed?: Uint8Array): Promise<HDKey> {
77
- seed ??= await mnemonicToSeed(mnemonic);
78
- // Generate HDKey from seed.
79
- const hdkey = HDKey.fromMasterSeed(seed);
80
- // Ensure HDKey returns valid
81
- if (!hdkey) {
82
- throw new Error('Failed to recover hdkey');
83
- }
84
- // Return the HDKey
85
- return hdkey;
86
- }
87
-
88
- /**
89
- * Recovers a secp256k1 privateKey from its original entropy
90
- * @param {Uint8Array} xorEntropy The original entropy to recover the privateKey from
91
- * @param {Uint8Array} salt The salt used to tweak the privateKey
92
- * @returns {Uint8Array} The recovered privateKey
93
- * @throws {Error} if the privateKey cannot be recovered
94
- */
95
- static recoverTweakedRawPrivateKey(xorEntropy: Uint8Array, salt: Uint8Array): Uint8Array {
96
- // If entropy is not 32 bytes, hash it to get a deterministic 32-byte private key
97
- if (xorEntropy.length !== 32) {
98
- xorEntropy = sha256(xorEntropy);
99
- }
100
- const entropy = this.XNOR(xorEntropy, salt);
101
- // Convert entropy to hex
102
- const hexEntropy = Buffer.from(entropy).toString('hex');
103
- // Convert hexEntropy to BigInt
104
- const privateKey = BigInt(`0x${hexEntropy}`);
105
- // Ensure private key is in valid secp256k1 range1
106
- if (privateKey < BigInt(1) || privateKey >= CURVE.n) {
107
- throw new Error('Invalid private key derived from entropy');
108
- }
109
- // The valid 32-byte private key
110
- return entropy;
111
- }
112
-
113
- /**
114
- * Recovers a secp256k1 privateKey from its original entropy
115
- * @param {Uint8Array} entropy The entropy to recover the privateKey from
116
- * @returns {Uint8Array} The recovered privateKey
117
- * @throws {Error} if the privateKey cannot be recovered
118
- */
119
- static recoverRawPrivateKey(entropy: Uint8Array): Uint8Array {
120
- // If entropy is not 32 bytes, hash it to get a deterministic 32-byte private key
121
- if (entropy.length !== 32) {
122
- entropy = sha256(entropy);
123
- }
124
- // Convert entropy to hex
125
- const hexEntropy = Buffer.from(entropy).toString('hex');
126
- // Convert hexEntropy to BigInt
127
- const privateKey = BigInt(`0x${hexEntropy}`);
128
- // Ensure private key is in valid secp256k1 range1
129
- if (privateKey < BigInt(1) || privateKey >= CURVE.n) {
130
- throw new Error('Invalid private key derived from entropy');
131
- }
132
- // The valid 32-byte private key
133
- return entropy;
134
- }
135
-
136
- /**
137
- * Tweak the entropy with a salt using XOR
138
- * @param {Uint8Array} entropy The entropy to tweak
139
- * @param {Uint8Array} salt The salt to tweak the entropy with
140
- * @returns {Uint8Array} The tweaked entropy
141
- */
142
- static XOR(entropy: Uint8Array, salt: Uint8Array): Uint8Array {
143
- const tweaked = new Uint8Array(entropy.length);
144
- for (let i = 0; i < entropy.length; i++) {
145
- tweaked[i] = entropy[i] ^ salt[i % salt.length]; // XOR with repeating salt
146
- }
147
- return tweaked;
148
- }
149
-
150
- /**
151
- * Untweak the entropy with a salt using XNOR
152
- *
153
- * @param {Uint8Array} tweakedEntropy The tweaked entropy to untweak
154
- * @param {Uint8Array} salt The salt to untweak the entropy with
155
- * @returns {Uint8Array} The original entropy
156
- */
157
- static XNOR(tweakedEntropy: Uint8Array, salt: Uint8Array): Uint8Array {
158
- const originalEntropy = new Uint8Array(tweakedEntropy.length);
159
- for (let i = 0; i < tweakedEntropy.length; i++) {
160
- originalEntropy[i] = tweakedEntropy[i] ^ salt[i % salt.length]; // XOR with salt again
161
- }
162
- return originalEntropy;
163
- }
164
-
165
- /**
166
- * Recovers an HDKey from a mnemonic phrase
167
- * @param {string} mnemonic The mnemonic phrase to recover the HDKey from
168
- * @param {string} path The path to derive the child key from
169
- * @returns {Uint8Array} Promise resolving to the recovered private key bytes
170
- * @throws {Error} if the HDKey cannot be recovered
171
- */
172
- static async recoverHdChildFromMnemonic(mnemonic: string, path: string): Promise<Uint8Array> {
173
- // Generate HDKey from seed.
174
- const hdkey = await this.recoverHdWallet(mnemonic);
175
- // Ensure HDKey returns valid
176
- if (!hdkey) {
177
- throw new Error('Failed to recover hdkey');
178
- }
179
- // Return the privateKey of the derived childKey
180
- const childPrivKeyBytes = hdkey.derive(path).privateKey;
181
- if (!childPrivKeyBytes) {
182
- throw new Error('Failed to recover child private key');
183
- }
184
- return childPrivKeyBytes;
185
- }
186
-
187
- /**
188
- * Derives a child key from an HDKey
189
- * @param {HDKey} hdkey The HDKey to derive the child key from
190
- * @param {string} path The path to derive the child key from
191
- * @returns {HDKey} A Promise resolving to the child key
192
- * @throws {Error} Error if the child key cannot be derived
193
- */
194
- static deriveChildKey(hdkey: HDKey, path: string): HDKey {
195
- // Derive child key from HDKey.
196
- const childKey = hdkey.derive(path);
197
- // Ensure child key returns valid
198
- if (!childKey) {
199
- throw new Error(`Failed to derive child key`);
200
- }
201
- // Return the child key
202
- return childKey;
203
- }
204
- }