@dwn-protocol/id-sdk 0.2.5 → 0.2.6

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 (99) hide show
  1. package/package.json +2 -3
  2. package/src/agent/app-data-store.ts +0 -365
  3. package/src/agent/did-manager.ts +0 -393
  4. package/src/agent/dwn-manager.ts +0 -548
  5. package/src/agent/identity-manager.ts +0 -165
  6. package/src/agent/index.ts +0 -19
  7. package/src/agent/json-rpc.ts +0 -107
  8. package/src/agent/key-manager.ts +0 -302
  9. package/src/agent/kms-local.ts +0 -412
  10. package/src/agent/outbox.ts +0 -128
  11. package/src/agent/rpc-client.ts +0 -223
  12. package/src/agent/store-managed-did.ts +0 -295
  13. package/src/agent/store-managed-identity.ts +0 -243
  14. package/src/agent/store-managed-key.ts +0 -754
  15. package/src/agent/sync-manager.ts +0 -631
  16. package/src/agent/test-managed-agent.ts +0 -299
  17. package/src/agent/types/agent.ts +0 -145
  18. package/src/agent/types/managed-key.ts +0 -442
  19. package/src/agent/utils.ts +0 -190
  20. package/src/common/convert.ts +0 -424
  21. package/src/common/index.ts +0 -9
  22. package/src/common/multicodec.ts +0 -176
  23. package/src/common/object.ts +0 -43
  24. package/src/common/stores.ts +0 -125
  25. package/src/common/stream-node.ts +0 -381
  26. package/src/common/stream.ts +0 -406
  27. package/src/common/type-utils.ts +0 -117
  28. package/src/common/types.ts +0 -48
  29. package/src/credentials/credential-bbs.ts +0 -419
  30. package/src/credentials/credential.ts +0 -324
  31. package/src/credentials/index.ts +0 -5
  32. package/src/credentials/presentation.ts +0 -182
  33. package/src/credentials/status-list.ts +0 -365
  34. package/src/credentials/utils.ts +0 -58
  35. package/src/credentials/validators.ts +0 -52
  36. package/src/crypto/algorithms-api/aes/base.ts +0 -49
  37. package/src/crypto/algorithms-api/aes/ctr.ts +0 -51
  38. package/src/crypto/algorithms-api/aes/index.ts +0 -2
  39. package/src/crypto/algorithms-api/crypto-algorithm.ts +0 -127
  40. package/src/crypto/algorithms-api/crypto-key.ts +0 -56
  41. package/src/crypto/algorithms-api/ec/base.ts +0 -39
  42. package/src/crypto/algorithms-api/ec/ecdh.ts +0 -53
  43. package/src/crypto/algorithms-api/ec/ecdsa.ts +0 -37
  44. package/src/crypto/algorithms-api/ec/eddsa.ts +0 -30
  45. package/src/crypto/algorithms-api/ec/index.ts +0 -4
  46. package/src/crypto/algorithms-api/errors.ts +0 -29
  47. package/src/crypto/algorithms-api/index.ts +0 -6
  48. package/src/crypto/algorithms-api/pbkdf/index.ts +0 -1
  49. package/src/crypto/algorithms-api/pbkdf/pbkdf2.ts +0 -91
  50. package/src/crypto/crypto-algorithms/aes-ctr.ts +0 -70
  51. package/src/crypto/crypto-algorithms/bbs.ts +0 -110
  52. package/src/crypto/crypto-algorithms/ecdh.ts +0 -115
  53. package/src/crypto/crypto-algorithms/ecdsa.ts +0 -111
  54. package/src/crypto/crypto-algorithms/eddsa.ts +0 -110
  55. package/src/crypto/crypto-algorithms/index.ts +0 -6
  56. package/src/crypto/crypto-algorithms/pbkdf2.ts +0 -54
  57. package/src/crypto/crypto-primitives/aes-ctr.ts +0 -131
  58. package/src/crypto/crypto-primitives/aes-gcm.ts +0 -138
  59. package/src/crypto/crypto-primitives/bbs.ts +0 -183
  60. package/src/crypto/crypto-primitives/concat-kdf.ts +0 -207
  61. package/src/crypto/crypto-primitives/ed25519.ts +0 -201
  62. package/src/crypto/crypto-primitives/index.ts +0 -10
  63. package/src/crypto/crypto-primitives/pbkdf2.ts +0 -78
  64. package/src/crypto/crypto-primitives/secp256k1.ts +0 -322
  65. package/src/crypto/crypto-primitives/x25519.ts +0 -101
  66. package/src/crypto/crypto-primitives/xchacha20-poly1305.ts +0 -46
  67. package/src/crypto/crypto-primitives/xchacha20.ts +0 -34
  68. package/src/crypto/index.ts +0 -8
  69. package/src/crypto/jose.ts +0 -948
  70. package/src/crypto/types/crypto-key.ts +0 -4
  71. package/src/crypto/types/iddwn-crypto.ts +0 -119
  72. package/src/crypto/utils.ts +0 -200
  73. package/src/did-api.ts +0 -72
  74. package/src/dids/dht.ts +0 -412
  75. package/src/dids/did-dht.ts +0 -436
  76. package/src/dids/did-ion.ts +0 -613
  77. package/src/dids/did-key.ts +0 -791
  78. package/src/dids/did-resolver.ts +0 -107
  79. package/src/dids/index.ts +0 -9
  80. package/src/dids/resolver-cache-level.ts +0 -82
  81. package/src/dids/resolver-cache-noop.ts +0 -25
  82. package/src/dids/types.ts +0 -278
  83. package/src/dids/utils.ts +0 -129
  84. package/src/dwn-api.ts +0 -584
  85. package/src/iddwn.ts +0 -241
  86. package/src/identity-agent/index.ts +0 -270
  87. package/src/index.ts +0 -26
  88. package/src/interfaces/metadata.ts +0 -163
  89. package/src/interfaces/queue.ts +0 -108
  90. package/src/interfaces/services.ts +0 -122
  91. package/src/interfaces/transactions.ts +0 -220
  92. package/src/protocol.ts +0 -68
  93. package/src/proxy-agent/index.ts +0 -255
  94. package/src/record.ts +0 -521
  95. package/src/service-options.ts +0 -62
  96. package/src/typings/decentralized-identity__ion-pow-sdk.d.ts +0 -7
  97. package/src/user-agent/index.ts +0 -295
  98. package/src/utils.ts +0 -29
  99. package/src/vc-api.ts +0 -505
@@ -1,631 +0,0 @@
1
- import type { AbstractBatchOperation, AbstractLevel } from 'abstract-level';
2
-
3
- import type {
4
- EventsGetReply,
5
- GenericMessage,
6
- MessagesGetReply,
7
- RecordsWriteMessage,
8
- } from '@dwn-protocol/id';
9
-
10
- import { Level } from 'level';
11
- import { Convert } from '../common/index.js';
12
- import { utils as didUtils } from '../dids/index.js';
13
- import { DataStream } from '@dwn-protocol/id';
14
-
15
- import type { IDManagedAgent } from './types/agent.js';
16
-
17
- import { webReadableToIsomorphicNodeReadable } from './utils.js';
18
-
19
- export interface SyncManager {
20
- agent: IDManagedAgent;
21
- registerIdentity(options: { did: string }): Promise<void>;
22
- runNow(): Promise<void>;
23
- startSync(options: { interval: number }): Promise<void>;
24
- stopSync(): void;
25
- push(): Promise<void>;
26
- pull(): Promise<void>;
27
- }
28
-
29
- type LevelDatabase = AbstractLevel<string | Buffer | Uint8Array, string, string>;
30
-
31
- export type SyncManagerOptions = {
32
- agent?: IDManagedAgent;
33
- dataPath?: string;
34
- db?: LevelDatabase;
35
- };
36
-
37
- type SyncDirection = 'push' | 'pull';
38
-
39
- type SyncState = {
40
- did: string;
41
- dwnUrl: string;
42
- watermark: string | undefined;
43
- }
44
-
45
- type DwnMessage = {
46
- message: any;
47
- data?: Blob;
48
- }
49
-
50
- type DbBatchOperation = AbstractBatchOperation<LevelDatabase, string, string>;
51
-
52
- const is2xx = (code: number) => code >= 200 && code <= 299;
53
- const is4xx = (code: number) => code >= 400 && code <= 499;
54
-
55
- export class SyncManagerLevel implements SyncManager {
56
- /**
57
- * Holds the instance of a `IDManagedAgent` that represents the current
58
- * execution context for the `KeyManager`. This agent is utilized
59
- * to interact with other agent components. It's vital
60
- * to ensure this instance is set to correctly contextualize
61
- * operations within the broader agent framework.
62
- */
63
- private _agent?: IDManagedAgent;
64
- private _db: LevelDatabase;
65
- private _syncIntervalId?: ReturnType<typeof setInterval>;
66
-
67
- constructor(options?: SyncManagerOptions) {
68
- let { agent, dataPath = 'data/AGENT/SYNC_STORE', db } = options ?? {};
69
-
70
- this._agent = agent;
71
- this._db = (db) ? db : new Level(dataPath);
72
- }
73
-
74
- /**
75
- * Retrieves the `IDManagedAgent` execution context.
76
- * If the `agent` instance proprety is undefined, it will throw an error.
77
- *
78
- * @returns The `IDManagedAgent` instance that represents the current execution
79
- * context.
80
- *
81
- * @throws Will throw an error if the `agent` instance property is undefined.
82
- */
83
- get agent(): IDManagedAgent {
84
- if (this._agent === undefined) {
85
- throw new Error('DidManager: Unable to determine agent execution context.');
86
- }
87
-
88
- return this._agent;
89
- }
90
-
91
- set agent(agent: IDManagedAgent) {
92
- this._agent = agent;
93
- }
94
-
95
- public async clear(): Promise<void> {
96
- await this._db.clear();
97
- }
98
-
99
- public async pull(): Promise<void> {
100
- const syncPeerState = await this.getSyncPeerState({ syncDirection: 'pull' });
101
- await this.enqueueOperations({ syncDirection: 'pull', syncPeerState });
102
-
103
- const pullQueue = this.getPullQueue();
104
- const pullJobs = await pullQueue.iterator().all();
105
-
106
- const deleteOperations: DbBatchOperation[] = [];
107
- const errored: Set<string> = new Set();
108
-
109
- for (let job of pullJobs) {
110
- const [key] = job;
111
- const [did, dwnUrl, watermark, messageCid] = key.split('~');
112
-
113
- // If a particular DWN service endpoint is unreachable, skip subsequent pull operations.
114
- if (errored.has(dwnUrl)) {
115
- continue;
116
- }
117
-
118
- const messageExists = await this.messageExists(did, messageCid);
119
- if (messageExists) {
120
- await this.setWatermark(did, dwnUrl, 'pull', watermark);
121
- deleteOperations.push({ type: 'del', key: key });
122
-
123
- continue;
124
- }
125
-
126
- const messagesGet = await this.agent.dwnManager.createMessage({
127
- author : did,
128
- messageType : 'MessagesGet',
129
- messageOptions : {
130
- messageCids: [messageCid]
131
- }
132
- });
133
-
134
- let reply: MessagesGetReply;
135
-
136
- try {
137
- reply = await this.agent.rpcClient.sendDwnRequest({
138
- dwnUrl,
139
- targetDid : did,
140
- message : messagesGet
141
- }) as MessagesGetReply;
142
- } catch(e) {
143
- errored.add(dwnUrl);
144
- continue;
145
- }
146
-
147
- for (let entry of reply.messages ?? []) {
148
- if (entry.error || !entry.message) {
149
-
150
- await this.setWatermark(did, dwnUrl, 'pull', watermark);
151
- await this.addMessage(did, messageCid);
152
- deleteOperations.push({ type: 'del', key: key });
153
-
154
- continue;
155
- }
156
-
157
- const messageType = this.getDwnMessageType(entry.message);
158
- let dataStream;
159
-
160
- if (messageType === 'RecordsWrite') {
161
- const { encodedData } = entry;
162
- const message = entry.message as RecordsWriteMessage;
163
-
164
- if (encodedData) {
165
- const dataBytes = Convert.base64Url(encodedData).toUint8Array();
166
- dataStream = DataStream.fromBytes(dataBytes);
167
- } else {
168
- const recordsRead = await this.agent.dwnManager.createMessage({
169
- author : did,
170
- messageType : 'RecordsRead',
171
- messageOptions : {
172
- filter: {
173
- recordId: message.recordId
174
- }
175
- }
176
- });
177
-
178
- const recordsReadReply = await this.agent.rpcClient.sendDwnRequest({
179
- dwnUrl,
180
- targetDid : did,
181
- message : recordsRead.message
182
- });
183
-
184
- const { record, status: readStatus } = recordsReadReply;
185
-
186
- if (is2xx(readStatus.code) && record) {
187
- /** If the read was successful, convert the data stream from web ReadableStream
188
- * to Node.js Readable so that the DWN can process it.*/
189
- dataStream = webReadableToIsomorphicNodeReadable(record.data as any);
190
-
191
- } else if (readStatus.code >= 400) {
192
- const pruneReply = await this.agent.dwnManager.writePrunedRecord({
193
- targetDid: did,
194
- message
195
- });
196
-
197
- if (pruneReply.status.code === 202 || pruneReply.status.code === 409) {
198
- await this.setWatermark(did, dwnUrl, 'pull', watermark);
199
- await this.addMessage(did, messageCid);
200
- deleteOperations.push({ type: 'del', key: key });
201
-
202
- continue;
203
- } else {
204
- throw new Error(`SyncManager: Failed to sync tombstone for message '${messageCid}'`);
205
- }
206
- }
207
- }
208
- }
209
-
210
- const pullReply = await this.agent.dwnManager.processMessage({
211
- targetDid : did,
212
- message : entry.message,
213
- dataStream
214
- });
215
-
216
- if (pullReply.status.code === 202 || pullReply.status.code === 409) {
217
- await this.setWatermark(did, dwnUrl, 'pull', watermark);
218
- await this.addMessage(did, messageCid);
219
- deleteOperations.push({ type: 'del', key: key });
220
- }
221
- }
222
- }
223
-
224
- await pullQueue.batch(deleteOperations as any);
225
- }
226
-
227
- public async push(): Promise<void> {
228
- const syncPeerState = await this.getSyncPeerState({ syncDirection: 'push' });
229
- await this.enqueueOperations({ syncDirection: 'push', syncPeerState });
230
-
231
- const pushQueue = this.getPushQueue();
232
- const pushJobs = await pushQueue.iterator().all();
233
-
234
- const deleteOperations: DbBatchOperation[] = [];
235
- const errored: Set<string> = new Set();
236
-
237
- for (let job of pushJobs) {
238
- const [key] = job;
239
- const [did, dwnUrl, watermark, messageCid] = key.split('~');
240
-
241
- // If a particular DWN service endpoint is unreachable, skip subsequent push operations.
242
- if (errored.has(dwnUrl)) {
243
- continue;
244
- }
245
-
246
- // If this message was already synced (e.g., pulled from remote), skip to prevent ping-pong.
247
- const messageExists = await this.messageExists(did, messageCid);
248
- if (messageExists) {
249
- deleteOperations.push({ type: 'del', key: key });
250
- await this.setWatermark(did, dwnUrl, 'push', watermark);
251
- continue;
252
- }
253
-
254
- // Attempt to retrieve the message from the local DWN.
255
- let dwnMessage: DwnMessage | undefined;
256
- try {
257
- dwnMessage = await this.getDwnMessage(did, messageCid);
258
- } catch {
259
- // If the message can't be retrieved, skip it and advance.
260
- deleteOperations.push({ type: 'del', key: key });
261
- await this.setWatermark(did, dwnUrl, 'push', watermark);
262
- await this.addMessage(did, messageCid);
263
- continue;
264
- }
265
-
266
- /** If the message does not exist on the local DWN, remove the sync operation from the
267
- * push queue, update the push watermark for this DID/DWN endpoint combination, add the
268
- * message to the local message store, and continue to the next job. */
269
- if (!dwnMessage) {
270
- deleteOperations.push({ type: 'del', key: key });
271
- await this.setWatermark(did, dwnUrl, 'push', watermark);
272
- await this.addMessage(did, messageCid);
273
-
274
- continue;
275
- }
276
-
277
- try {
278
- const reply = await this.agent.rpcClient.sendDwnRequest({
279
- dwnUrl,
280
- targetDid : did,
281
- data : dwnMessage.data,
282
- message : dwnMessage.message
283
- });
284
-
285
- /** Update the watermark and add the messageCid to the Sync Message Store if either:
286
- * - 202: message was successfully written to the remote DWN
287
- * - 409: message was already present on the remote DWN
288
- */
289
- if (reply.status.code === 202 || reply.status.code === 409) {
290
- await this.setWatermark(did, dwnUrl, 'push', watermark);
291
- await this.addMessage(did, messageCid);
292
- deleteOperations.push({ type: 'del', key: key });
293
- }
294
- } catch {
295
- // Error is intentionally ignored; 'errored' set is updated with 'dwnUrl'.
296
- errored.add(dwnUrl);
297
- }
298
- }
299
-
300
- await pushQueue.batch(deleteOperations as any);
301
- }
302
-
303
- public async registerIdentity(options: {
304
- did: string
305
- }): Promise<void> {
306
- const { did } = options;
307
-
308
- const registeredIdentities = this._db.sublevel('registeredIdentities');
309
-
310
- await registeredIdentities.put(did, '');
311
- }
312
-
313
- public startSync(options: {
314
- interval: number
315
- }): Promise<void> {
316
- const { interval = 60_000 } = options;
317
-
318
- if (this._syncIntervalId) {
319
- clearInterval(this._syncIntervalId);
320
- }
321
-
322
- this._syncIntervalId = setInterval(async () => {
323
- try {
324
- await this.push();
325
- } catch (error) {
326
- console.error('SyncManager: push error:', error);
327
- }
328
-
329
- try {
330
- await this.pull();
331
- } catch (error) {
332
- console.error('SyncManager: pull error:', error);
333
- }
334
- }, interval);
335
-
336
- return Promise.resolve();
337
- }
338
-
339
- /**
340
- * Run one cycle of push, pull, and outbox drain. Used by the sync interval
341
- * and by flushOutboxAndSync() for on-demand sync.
342
- */
343
- public async runNow(): Promise<void> {
344
- await this.push();
345
- await this.pull();
346
- await this.agent.outbox?.drain();
347
- }
348
-
349
- public stopSync(): void {
350
- if (this._syncIntervalId) {
351
- clearInterval(this._syncIntervalId);
352
- this._syncIntervalId = undefined;
353
- }
354
- }
355
-
356
- private async enqueueOperations(options: {
357
- syncDirection: SyncDirection,
358
- syncPeerState: SyncState[]
359
- }) {
360
- const { syncDirection, syncPeerState } = options;
361
-
362
- for (let syncState of syncPeerState) {
363
- // Get the event log from the remote DWN if pull sync, or local DWN if push sync.
364
- const eventLog = await this.getDwnEventLog({
365
- did : syncState.did,
366
- dwnUrl : syncState.dwnUrl,
367
- syncDirection,
368
- watermark : syncState.watermark
369
- });
370
-
371
- const syncOperations: DbBatchOperation[] = [];
372
-
373
- for (let event of eventLog) {
374
- /** Use "did~dwnUrl~watermark~messageCid" as the key in the sync queue.
375
- * Note: It is critical that `watermark` precedes `messageCid` to
376
- * ensure that when the sync jobs are pulled off the queue, they
377
- * are lexographically sorted oldest to newest. */
378
- const operationKey = [
379
- syncState.did,
380
- syncState.dwnUrl,
381
- event.watermark,
382
- event.messageCid
383
- ].join('~');
384
-
385
- const operation: DbBatchOperation = { type: 'put', key: operationKey, value: '' };
386
-
387
- syncOperations.push(operation);
388
- }
389
-
390
- if (syncOperations.length > 0) {
391
- const syncQueue = (syncDirection === 'pull')
392
- ? this.getPullQueue()
393
- : this.getPushQueue();
394
- await syncQueue.batch(syncOperations as any);
395
- }
396
- }
397
- }
398
-
399
- private async getDwnEventLog(options: {
400
- did: string,
401
- dwnUrl: string,
402
- syncDirection: SyncDirection,
403
- watermark?: string
404
- }) {
405
- const { did, dwnUrl, syncDirection, watermark } = options;
406
-
407
- let eventsReply = {} as EventsGetReply;
408
-
409
- if (syncDirection === 'pull') {
410
- // When sync is a pull, get the event log from the remote DWN.
411
- const eventsGetMessage = await this.agent.dwnManager.createMessage({
412
- author : did,
413
- messageType : 'EventsGet',
414
- messageOptions : { watermark }
415
- });
416
-
417
- try {
418
- eventsReply = await this.agent.rpcClient.sendDwnRequest({
419
- dwnUrl : dwnUrl,
420
- targetDid : did,
421
- message : eventsGetMessage
422
- });
423
- } catch {
424
- // If a particular DWN service endpoint is unreachable, silently ignore.
425
- }
426
-
427
- } else if (syncDirection === 'push') {
428
- // When sync is a push, get the event log from the local DWN.
429
- ({ reply: eventsReply } = await this.agent.dwnManager.processRequest({
430
- author : did,
431
- target : did,
432
- messageType : 'EventsGet',
433
- messageOptions : { watermark }
434
- }));
435
- }
436
-
437
- const eventLog = eventsReply.events ?? [];
438
-
439
- return eventLog;
440
- }
441
-
442
- private async getDwnMessage(
443
- author: string,
444
- messageCid: string
445
- ): Promise<DwnMessage | undefined> {
446
- let messagesGetResponse = await this.agent.dwnManager.processRequest({
447
- author : author,
448
- target : author,
449
- messageType : 'MessagesGet',
450
- messageOptions : {
451
- messageCids: [messageCid]
452
- }
453
- });
454
-
455
- const reply: MessagesGetReply = messagesGetResponse.reply;
456
-
457
- /** Absence of a messageEntry or message within messageEntry can happen because updating a
458
- * Record creates another RecordsWrite with the same recordId. Only the first and
459
- * most recent RecordsWrite messages are kept for a given recordId. Any RecordsWrite messages
460
- * that aren't the first or most recent are discarded by the DWN. */
461
- if (!(reply.messages && reply.messages.length === 1)) {
462
- return undefined;
463
- }
464
-
465
- const [ messageEntry ] = reply.messages;
466
-
467
- let { message } = messageEntry;
468
- if (!message) {
469
- return undefined;
470
- }
471
-
472
- let dwnMessage: DwnMessage = { message };
473
- const messageType = `${message.descriptor.interface}${message.descriptor.method}`;
474
-
475
- // if the message is a RecordsWrite, either data will be present, OR we have to get it using a RecordsRead
476
- if (messageType === 'RecordsWrite') {
477
- const { encodedData } = messageEntry;
478
- const writeMessage = message as RecordsWriteMessage;
479
-
480
- if (encodedData) {
481
- const dataBytes: any = Convert.base64Url(encodedData).toUint8Array();
482
- dwnMessage.data = new Blob([dataBytes]);
483
- } else {
484
- let readResponse = await this.agent.dwnManager.processRequest({
485
- author : author,
486
- target : author,
487
- messageType : 'RecordsRead',
488
- messageOptions : {
489
- filter: {
490
- recordId: writeMessage.recordId
491
- }
492
- }
493
- });
494
- const reply = readResponse.reply;
495
-
496
- if (is2xx(reply.status.code) && reply.record) {
497
- // If status code is 200-299, return the data.
498
- const dataBytes: any = await DataStream.toBytes(reply.record.data);
499
- dwnMessage.data = new Blob([dataBytes]);
500
-
501
- } else if (is4xx(reply.status.code)) {
502
- /** If status code is 400-499, typically 404 indicating the data no longer exists, it is
503
- * likely that a `RecordsDelete` took place. `RecordsDelete` keeps a `RecordsWrite` and
504
- * deletes the associated data, effectively acting as a "tombstone." Sync still needs to
505
- * _push_ this tombstone so that the `RecordsDelete` can be processed successfully. */
506
-
507
- } else {
508
- // If status code is anything else (likely 5xx), throw an error.
509
- const { status } = reply;
510
- throw new Error(`SyncManager: Failed to read data associated with record ${writeMessage.recordId}. (${status.code}) ${status.detail}}`);
511
- }
512
- }
513
- }
514
-
515
- return dwnMessage;
516
- }
517
-
518
- private async getSyncPeerState(options: {
519
- syncDirection: SyncDirection
520
- }): Promise<SyncState[]> {
521
- const { syncDirection } = options;
522
-
523
- // Get a list of the DIDs of all registered identities.
524
- const registeredIdentities = await this._db.sublevel('registeredIdentities').keys().all();
525
-
526
- // Array to accumulate the list of sync peers for each DID.
527
- const syncPeerState: SyncState[] = [];
528
-
529
- for (let did of registeredIdentities) {
530
- // Resolve the DID to its DID document.
531
- const { didDocument, didResolutionMetadata } = await this.agent.didResolver.resolve(did);
532
-
533
- // If DID resolution fails, throw an error.
534
- if (!didDocument) {
535
- const errorCode = `${didResolutionMetadata?.error}: ` || '';
536
- const defaultMessage = `Unable to resolve DID: ${did}`;
537
- const errorMessage = didResolutionMetadata?.errorMessage ?? defaultMessage;
538
- throw new Error(`SyncManager: ${errorCode}${errorMessage}`);
539
- }
540
-
541
- // Attempt to get the `#dwn` service entry from the DID document.
542
- const [ service ] = didUtils.getServices({ didDocument, id: '#dwn' });
543
-
544
- /** Silently ignore and do not try to perform Sync for any DID that does not have a DWN
545
- * service endpoint published in its DID document. **/
546
- if (!service) {
547
- continue;
548
- }
549
-
550
- if (!didUtils.isDwnServiceEndpoint(service.serviceEndpoint)) {
551
- throw new Error(`SyncManager: Malformed '#dwn' service endpoint. Expected array of node addresses.`);
552
- }
553
-
554
- /** Get the watermark (or undefined) for each (DID, DWN service endpoint, sync direction)
555
- * combination and add it to the sync peer state array. */
556
- for (let dwnUrl of service.serviceEndpoint.nodes) {
557
- const watermark = await this.getWatermark(did, dwnUrl, syncDirection);
558
- syncPeerState.push({ did, dwnUrl, watermark });
559
- }
560
- }
561
-
562
- return syncPeerState;
563
- }
564
-
565
- private async getWatermark(did: string, dwnUrl: string, direction: SyncDirection) {
566
- const wmKey = `${did}~${dwnUrl}~${direction}`;
567
- const watermarkStore = this.getWatermarkStore();
568
-
569
- try {
570
- return await watermarkStore.get(wmKey);
571
- } catch(error: any) {
572
- // Don't throw when a key wasn't found.
573
- if (error.notFound) {
574
- return undefined;
575
- }
576
- }
577
- }
578
-
579
- private async setWatermark(did: string, dwnUrl: string, direction: SyncDirection, watermark: string) {
580
- const wmKey = `${did}~${dwnUrl}~${direction}`;
581
- const watermarkStore = this.getWatermarkStore();
582
-
583
- await watermarkStore.put(wmKey, watermark);
584
- }
585
-
586
- /**
587
- * The message store is used to prevent "echoes" that occur during a sync pull operation.
588
- * After a message is confirmed to already be synchronized on the local DWN, its CID is added
589
- * to the message store to ensure that any subsequent pull attempts are skipped.
590
- */
591
- private async messageExists(did: string, messageCid: string) {
592
- const messageStore = this.getMessageStore(did);
593
-
594
- // If the `messageCid` exists in this DID's store, return true. Otherwise, return false.
595
- try {
596
- await messageStore.get(messageCid);
597
- return true;
598
- } catch (error: any) {
599
- if (error.notFound) {
600
- return false;
601
- }
602
- throw error;
603
- }
604
- }
605
-
606
- private async addMessage(did: string, messageCid: string) {
607
- const messageStore = this.getMessageStore(did);
608
-
609
- return await messageStore.put(messageCid, '');
610
- }
611
-
612
- private getMessageStore(did: string) {
613
- return this._db.sublevel('history').sublevel(did).sublevel('messages');
614
- }
615
-
616
- private getWatermarkStore() {
617
- return this._db.sublevel('watermarks');
618
- }
619
-
620
- private getPushQueue() {
621
- return this._db.sublevel('pushQueue');
622
- }
623
-
624
- private getPullQueue() {
625
- return this._db.sublevel('pullQueue');
626
- }
627
-
628
- private getDwnMessageType(message: GenericMessage) {
629
- return `${message.descriptor.interface}${message.descriptor.method}`;
630
- }
631
- }