@dxos/echo-db 2.33.8-dev.8609bc45 → 2.33.9-dev.002e8917

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 (189) hide show
  1. package/dist/src/api/result-set.js +4 -4
  2. package/dist/src/api/result-set.js.map +1 -1
  3. package/dist/src/echo.js +2 -2
  4. package/dist/src/echo.js.map +1 -1
  5. package/dist/src/echo.test.js +13 -13
  6. package/dist/src/echo.test.js.map +1 -1
  7. package/dist/src/halo/halo-factory.d.ts +1 -1
  8. package/dist/src/halo/halo-factory.d.ts.map +1 -1
  9. package/dist/src/halo/halo-factory.js +16 -16
  10. package/dist/src/halo/halo-factory.js.map +1 -1
  11. package/dist/src/halo/halo-party.d.ts +4 -6
  12. package/dist/src/halo/halo-party.d.ts.map +1 -1
  13. package/dist/src/halo/halo-party.js +12 -6
  14. package/dist/src/halo/halo-party.js.map +1 -1
  15. package/dist/src/halo/halo.d.ts.map +1 -1
  16. package/dist/src/halo/halo.js +11 -10
  17. package/dist/src/halo/halo.js.map +1 -1
  18. package/dist/src/halo/identity-manager.d.ts.map +1 -1
  19. package/dist/src/halo/identity-manager.js +13 -7
  20. package/dist/src/halo/identity-manager.js.map +1 -1
  21. package/dist/src/halo/identity.js +2 -2
  22. package/dist/src/halo/identity.js.map +1 -1
  23. package/dist/src/halo/party-opener.d.ts.map +1 -1
  24. package/dist/src/halo/party-opener.js +2 -4
  25. package/dist/src/halo/party-opener.js.map +1 -1
  26. package/dist/src/halo/preferences.d.ts.map +1 -1
  27. package/dist/src/halo/preferences.js +9 -12
  28. package/dist/src/halo/preferences.js.map +1 -1
  29. package/dist/src/invitations/greeting-initiator.d.ts +5 -4
  30. package/dist/src/invitations/greeting-initiator.d.ts.map +1 -1
  31. package/dist/src/invitations/greeting-initiator.js +9 -10
  32. package/dist/src/invitations/greeting-initiator.js.map +1 -1
  33. package/dist/src/invitations/greeting-protocol-provider.js +2 -2
  34. package/dist/src/invitations/greeting-protocol-provider.js.map +1 -1
  35. package/dist/src/invitations/greeting-responder.d.ts +4 -7
  36. package/dist/src/invitations/greeting-responder.d.ts.map +1 -1
  37. package/dist/src/invitations/greeting-responder.js +12 -26
  38. package/dist/src/invitations/greeting-responder.js.map +1 -1
  39. package/dist/src/invitations/halo-recovery-initiator.js +8 -8
  40. package/dist/src/invitations/halo-recovery-initiator.js.map +1 -1
  41. package/dist/src/invitations/invitation-descriptor.js +12 -12
  42. package/dist/src/invitations/invitation-descriptor.js.map +1 -1
  43. package/dist/src/invitations/invitation-factory.d.ts +2 -1
  44. package/dist/src/invitations/invitation-factory.d.ts.map +1 -1
  45. package/dist/src/invitations/invitation-factory.js +6 -8
  46. package/dist/src/invitations/invitation-factory.js.map +1 -1
  47. package/dist/src/invitations/offline-invitation-claimer.js +9 -9
  48. package/dist/src/invitations/offline-invitation-claimer.js.map +1 -1
  49. package/dist/src/packlets/database/data-mirror.js +8 -8
  50. package/dist/src/packlets/database/data-mirror.js.map +1 -1
  51. package/dist/src/packlets/database/data-mirror.test.js +1 -1
  52. package/dist/src/packlets/database/data-mirror.test.js.map +1 -1
  53. package/dist/src/packlets/database/data-service-host.js +3 -3
  54. package/dist/src/packlets/database/data-service-host.js.map +1 -1
  55. package/dist/src/packlets/database/data-service-router.js +5 -5
  56. package/dist/src/packlets/database/data-service-router.js.map +1 -1
  57. package/dist/src/packlets/database/database-backend.js +3 -3
  58. package/dist/src/packlets/database/database-backend.js.map +1 -1
  59. package/dist/src/packlets/database/database.js +2 -2
  60. package/dist/src/packlets/database/database.js.map +1 -1
  61. package/dist/src/packlets/database/item-demuxer.js +14 -14
  62. package/dist/src/packlets/database/item-demuxer.js.map +1 -1
  63. package/dist/src/packlets/database/item-demuxer.test.js +1 -1
  64. package/dist/src/packlets/database/item-demuxer.test.js.map +1 -1
  65. package/dist/src/packlets/database/item-manager.js +18 -18
  66. package/dist/src/packlets/database/item-manager.js.map +1 -1
  67. package/dist/src/packlets/database/link.js +3 -3
  68. package/dist/src/packlets/database/link.js.map +1 -1
  69. package/dist/src/packlets/database/selection/result.js +2 -2
  70. package/dist/src/packlets/database/selection/result.js.map +1 -1
  71. package/dist/src/packlets/database/testing.js +1 -1
  72. package/dist/src/packlets/database/testing.js.map +1 -1
  73. package/dist/src/packlets/database/timeframe-clock.d.ts +2 -1
  74. package/dist/src/packlets/database/timeframe-clock.d.ts.map +1 -1
  75. package/dist/src/packlets/database/timeframe-clock.js +15 -5
  76. package/dist/src/packlets/database/timeframe-clock.js.map +1 -1
  77. package/dist/src/parties/data-party.d.ts +4 -3
  78. package/dist/src/parties/data-party.d.ts.map +1 -1
  79. package/dist/src/parties/data-party.js +18 -14
  80. package/dist/src/parties/data-party.js.map +1 -1
  81. package/dist/src/parties/data-party.test.js +12 -12
  82. package/dist/src/parties/data-party.test.js.map +1 -1
  83. package/dist/src/parties/party-factory.d.ts +2 -4
  84. package/dist/src/parties/party-factory.d.ts.map +1 -1
  85. package/dist/src/parties/party-factory.js +18 -15
  86. package/dist/src/parties/party-factory.js.map +1 -1
  87. package/dist/src/parties/party-manager.d.ts +1 -1
  88. package/dist/src/parties/party-manager.d.ts.map +1 -1
  89. package/dist/src/parties/party-manager.js +19 -19
  90. package/dist/src/parties/party-manager.js.map +1 -1
  91. package/dist/src/parties/party-manager.test.js +22 -18
  92. package/dist/src/parties/party-manager.test.js.map +1 -1
  93. package/dist/src/parties/party-preferences.js +2 -2
  94. package/dist/src/parties/party-preferences.js.map +1 -1
  95. package/dist/src/pipeline/feed-muxer.d.ts.map +1 -1
  96. package/dist/src/pipeline/feed-muxer.js +5 -5
  97. package/dist/src/pipeline/feed-muxer.js.map +1 -1
  98. package/dist/src/pipeline/feed-muxer.test.js +2 -2
  99. package/dist/src/pipeline/feed-muxer.test.js.map +1 -1
  100. package/dist/src/pipeline/message-selector.d.ts +1 -2
  101. package/dist/src/pipeline/message-selector.d.ts.map +1 -1
  102. package/dist/src/pipeline/message-selector.js +6 -33
  103. package/dist/src/pipeline/message-selector.js.map +1 -1
  104. package/dist/src/pipeline/metadata-store.d.ts +7 -2
  105. package/dist/src/pipeline/metadata-store.d.ts.map +1 -1
  106. package/dist/src/pipeline/metadata-store.js +12 -3
  107. package/dist/src/pipeline/metadata-store.js.map +1 -1
  108. package/dist/src/pipeline/party-feed-provider.d.ts +2 -2
  109. package/dist/src/pipeline/party-feed-provider.d.ts.map +1 -1
  110. package/dist/src/pipeline/party-feed-provider.js +5 -4
  111. package/dist/src/pipeline/party-feed-provider.js.map +1 -1
  112. package/dist/src/pipeline/party-pipeline.d.ts +5 -5
  113. package/dist/src/pipeline/party-pipeline.d.ts.map +1 -1
  114. package/dist/src/pipeline/party-pipeline.js +19 -20
  115. package/dist/src/pipeline/party-pipeline.js.map +1 -1
  116. package/dist/src/pipeline/party-pipeline.test.js +13 -18
  117. package/dist/src/pipeline/party-pipeline.test.js.map +1 -1
  118. package/dist/src/pipeline/party-processor.js +3 -3
  119. package/dist/src/pipeline/party-processor.js.map +1 -1
  120. package/dist/src/protocol/authenticator.test.js +53 -12
  121. package/dist/src/protocol/authenticator.test.js.map +1 -1
  122. package/dist/src/protocol/identity-credentials.d.ts.map +1 -1
  123. package/dist/src/protocol/identity-credentials.js +2 -2
  124. package/dist/src/protocol/identity-credentials.js.map +1 -1
  125. package/dist/src/protocol/party-protocol-factory.js +1 -1
  126. package/dist/src/protocol/party-protocol-factory.js.map +1 -1
  127. package/dist/src/protocol/replicator-plugin.d.ts.map +1 -1
  128. package/dist/src/protocol/replicator-plugin.js +1 -2
  129. package/dist/src/protocol/replicator-plugin.js.map +1 -1
  130. package/dist/src/snapshots/snapshot-generator.js +2 -2
  131. package/dist/src/snapshots/snapshot-generator.js.map +1 -1
  132. package/dist/src/snapshots/snapshot-store.d.ts.map +1 -1
  133. package/dist/src/snapshots/snapshot-store.js +4 -4
  134. package/dist/src/snapshots/snapshot-store.js.map +1 -1
  135. package/dist/src/testing/testing-factories.js +2 -2
  136. package/dist/src/testing/testing-factories.js.map +1 -1
  137. package/dist/tsconfig.tsbuildinfo +1 -1
  138. package/package.json +24 -20
  139. package/src/api/result-set.ts +1 -1
  140. package/src/echo.test.ts +3 -3
  141. package/src/echo.ts +1 -1
  142. package/src/halo/halo-factory.ts +8 -9
  143. package/src/halo/halo-party.ts +15 -7
  144. package/src/halo/halo.ts +4 -2
  145. package/src/halo/identity-manager.ts +12 -3
  146. package/src/halo/identity.ts +1 -1
  147. package/src/halo/party-opener.ts +2 -4
  148. package/src/halo/preferences.ts +5 -9
  149. package/src/invitations/greeting-initiator.ts +11 -6
  150. package/src/invitations/greeting-protocol-provider.ts +2 -2
  151. package/src/invitations/greeting-responder.ts +12 -31
  152. package/src/invitations/halo-recovery-initiator.ts +4 -4
  153. package/src/invitations/invitation-descriptor.ts +6 -6
  154. package/src/invitations/invitation-factory.ts +3 -3
  155. package/src/invitations/offline-invitation-claimer.ts +4 -4
  156. package/src/packlets/database/data-mirror.test.ts +2 -2
  157. package/src/packlets/database/data-mirror.ts +1 -1
  158. package/src/packlets/database/data-service-host.ts +1 -1
  159. package/src/packlets/database/data-service-router.ts +1 -1
  160. package/src/packlets/database/database-backend.ts +1 -1
  161. package/src/packlets/database/database.ts +1 -1
  162. package/src/packlets/database/item-demuxer.test.ts +2 -2
  163. package/src/packlets/database/item-demuxer.ts +1 -1
  164. package/src/packlets/database/item-manager.ts +1 -1
  165. package/src/packlets/database/link.ts +1 -1
  166. package/src/packlets/database/selection/result.ts +1 -1
  167. package/src/packlets/database/testing.ts +2 -2
  168. package/src/packlets/database/timeframe-clock.ts +4 -1
  169. package/src/parties/data-party.test.ts +12 -12
  170. package/src/parties/data-party.ts +15 -7
  171. package/src/parties/party-factory.ts +21 -13
  172. package/src/parties/party-manager.test.ts +13 -9
  173. package/src/parties/party-manager.ts +15 -13
  174. package/src/parties/party-preferences.ts +1 -1
  175. package/src/pipeline/feed-muxer.test.ts +2 -2
  176. package/src/pipeline/feed-muxer.ts +5 -5
  177. package/src/pipeline/message-selector.ts +5 -36
  178. package/src/pipeline/metadata-store.ts +53 -31
  179. package/src/pipeline/party-feed-provider.ts +3 -3
  180. package/src/pipeline/party-pipeline.test.ts +13 -19
  181. package/src/pipeline/party-pipeline.ts +14 -13
  182. package/src/pipeline/party-processor.ts +1 -1
  183. package/src/protocol/authenticator.test.ts +102 -17
  184. package/src/protocol/identity-credentials.ts +5 -2
  185. package/src/protocol/party-protocol-factory.ts +2 -2
  186. package/src/protocol/replicator-plugin.ts +1 -2
  187. package/src/snapshots/snapshot-generator.ts +1 -1
  188. package/src/snapshots/snapshot-store.ts +3 -3
  189. package/src/testing/testing-factories.ts +1 -1
@@ -2,17 +2,16 @@
2
2
  // Copyright 2020 DXOS.org
3
3
  //
4
4
 
5
- import assert from 'assert';
6
5
  import debug from 'debug';
6
+ import assert from 'node:assert';
7
7
 
8
8
  import { Event } from '@dxos/async';
9
9
  import { Message as HaloMessage } from '@dxos/credentials';
10
- import { keyToString } from '@dxos/crypto';
11
10
  import { checkType } from '@dxos/debug';
12
11
  import {
13
- createFeedMeta, EchoEnvelope, FeedMessage, FeedStoreIterator, FeedWriter, IEchoStream, mapFeedWriter, Timeframe
12
+ createFeedMeta, EchoEnvelope, FeedMessage, FeedStoreIterator, FeedWriter, IEchoStream, mapFeedWriter
14
13
  } from '@dxos/echo-protocol';
15
- import { PublicKey } from '@dxos/protocols';
14
+ import { PublicKey, Timeframe } from '@dxos/protocols';
16
15
  import { jsonReplacer } from '@dxos/util';
17
16
 
18
17
  import { EchoProcessor, TimeframeClock } from '../packlets/database';
@@ -139,7 +138,8 @@ export class FeedMuxer {
139
138
 
140
139
  if (message.echo) {
141
140
  const memberKey = this._partyProcessor.getFeedOwningMember(PublicKey.from(block.key));
142
- assert(memberKey, `Ownership of feed ${keyToString(block.key)} could not be determined.`);
141
+ // TODO(wittjosiah): Is actually a Buffer for some reason. See todo in IFeedGenericBlock.
142
+ assert(memberKey, `Ownership of feed ${PublicKey.stringify(block.key as unknown as Buffer)} could not be determined.`);
143
143
 
144
144
  // Validate messge.
145
145
  const { itemId } = message.echo;
@@ -2,15 +2,12 @@
2
2
  // Copyright 2020 DXOS.org
3
3
  //
4
4
 
5
- import assert from 'assert';
6
5
  import debug from 'debug';
6
+ import assert from 'node:assert';
7
7
 
8
- import { getPartyCredentialMessageType, PartyCredential } from '@dxos/credentials';
9
8
  import { MessageSelector } from '@dxos/echo-protocol';
10
- import { PublicKey } from '@dxos/protocols';
11
9
 
12
10
  import { TimeframeClock } from '../packlets/database';
13
- import { PartyStateProvider } from './party-processor';
14
11
 
15
12
  const log = debug('dxos:echo-db:message-selector');
16
13
 
@@ -23,44 +20,16 @@ const log = debug('dxos:echo-db:message-selector');
23
20
  * @param partyProcessor
24
21
  * @param timeframeClock
25
22
  */
26
- export const createMessageSelector = (partyProcessor: PartyStateProvider, timeframeClock: TimeframeClock): MessageSelector => candidates => {
27
- // Check ECHO message candidates first since they are less expensive than HALO cancidates.
23
+ export const createMessageSelector = (timeframeClock: TimeframeClock): MessageSelector => candidates => {
24
+ // Pick the first candidate with a valid timeframe that has no gaps.
28
25
  for (let i = 0; i < candidates.length; i++) {
29
- const { data: { timeframe, echo } } = candidates[i];
30
- const feedKey = PublicKey.from(candidates[i].key);
31
- if (!echo) {
32
- continue;
33
- }
34
-
35
- assert(timeframe);
36
- if (partyProcessor.isFeedAdmitted(feedKey) && !timeframeClock.hasGaps(timeframe)) {
37
- return i;
38
- }
39
- }
40
-
41
- // Check HALO message candidates.
42
- for (let i = 0; i < candidates.length; i++) {
43
- const { data: { timeframe, halo } } = candidates[i];
44
- const feedKey = PublicKey.from(candidates[i].key);
45
- if (!halo) {
46
- continue;
47
- }
26
+ const { data: { timeframe } } = candidates[i];
48
27
 
49
28
  assert(timeframe);
50
- if (partyProcessor.isFeedAdmitted(feedKey) && !timeframeClock.hasGaps(timeframe)) {
29
+ if (!timeframeClock.hasGaps(timeframe)) {
51
30
  return i;
52
31
  }
53
-
54
- if (partyProcessor.genesisRequired) {
55
- try { // TODO(dmaretskyi): Get getPartyCredentialMessageType crashes for some reason.
56
- // TODO(telackey): Add check that this is for the right Party.
57
- if (getPartyCredentialMessageType(halo) === PartyCredential.Type.PARTY_GENESIS) {
58
- return i;
59
- }
60
- } catch { }
61
- }
62
32
  }
63
-
64
33
  // Not ready for this message yet.
65
34
  log('Skipping...');
66
35
  };
@@ -2,13 +2,13 @@
2
2
  // Copyright 2021 DXOS.org
3
3
  //
4
4
 
5
- import assert from 'assert';
6
5
  import debug from 'debug';
6
+ import assert from 'node:assert';
7
7
 
8
8
  import { synchronized } from '@dxos/async';
9
9
  import { failUndefined } from '@dxos/debug';
10
- import { EchoMetadata, PartyMetadata, schema, Timeframe } from '@dxos/echo-protocol';
11
- import { PublicKey } from '@dxos/protocols';
10
+ import { EchoMetadata, PartyMetadata, schema } from '@dxos/echo-protocol';
11
+ import { PublicKey, Timeframe } from '@dxos/protocols';
12
12
  import { Directory } from '@dxos/random-access-multi-storage';
13
13
 
14
14
  /**
@@ -20,13 +20,20 @@ export const STORAGE_VERSION = 1;
20
20
 
21
21
  const log = debug('dxos:snapshot-store');
22
22
 
23
+ export interface AddPartyOptions {
24
+ key: PublicKey
25
+ genesisFeed: PublicKey
26
+ }
27
+
28
+ const emptyEchoMetadata = (): EchoMetadata => ({
29
+ version: STORAGE_VERSION,
30
+ parties: [],
31
+ created: new Date(),
32
+ updated: new Date()
33
+ });
34
+
23
35
  export class MetadataStore {
24
- private _metadata: EchoMetadata = {
25
- version: STORAGE_VERSION,
26
- parties: [],
27
- created: new Date(),
28
- updated: new Date()
29
- };
36
+ private _metadata: EchoMetadata = emptyEchoMetadata();
30
37
 
31
38
  constructor (
32
39
  private readonly _directory: Directory
@@ -51,19 +58,26 @@ export class MetadataStore {
51
58
  async load (): Promise<void> {
52
59
  const file = this._directory.createOrOpen('EchoMetadata');
53
60
  try {
54
- const { size } = await file.stat();
55
- if (size === 0) {
61
+ const { size: fileLength } = await file.stat();
62
+ if (fileLength < 4) {
56
63
  return;
57
64
  }
65
+ // Loading file size from first 4 bytes.
66
+ const dataSize = fromBytesInt32(await file.read(0, 4));
67
+ log(`Load: data size ${dataSize}`);
68
+
69
+ // Sanity check.
70
+ {
71
+ if (fileLength < dataSize + 4) {
72
+ throw new Error('Metadata storage is corrupted');
73
+ }
74
+ }
58
75
 
59
- const data = await file.read(0, size);
76
+ const data = await file.read(4, dataSize);
60
77
  this._metadata = schema.getCodecForType('dxos.echo.metadata.EchoMetadata').decode(data);
61
78
  } catch (err: any) {
62
- if (err.code === 'ENOENT') {
63
- return;
64
- } else {
65
- throw err;
66
- }
79
+ log(`Error loading metadata: ${err}`);
80
+ this._metadata = emptyEchoMetadata();
67
81
  } finally {
68
82
  await file.close();
69
83
  }
@@ -82,22 +96,14 @@ export class MetadataStore {
82
96
 
83
97
  try {
84
98
  const encoded = Buffer.from(schema.getCodecForType('dxos.echo.metadata.EchoMetadata').encode(data));
85
- await file.write(0, encoded);
86
99
 
87
- // Truncate the rest of the file.
88
- {
89
- const { size } = await file.stat();
90
- if (size > encoded.length) {
91
- await file.truncate(encoded.length, size);
92
- }
93
- }
100
+ // Saving file size at first 4 bytes.
101
+ log(`Save: data size ${encoded.length}`);
102
+ await file.write(0, toBytesInt32(encoded.length));
103
+
104
+ // Saving data.
105
+ await file.write(4, encoded);
94
106
 
95
- // Sanity check.
96
- const { size } = await file.stat();
97
- if (size !== encoded.length) {
98
- console.log('SANITY!');
99
- }
100
- assert(size === encoded.length);
101
107
  } finally {
102
108
  await file.close();
103
109
  }
@@ -147,6 +153,14 @@ export class MetadataStore {
147
153
  await this._save();
148
154
  }
149
155
 
156
+ async setGenesisFeed (partyKey: PublicKey, feedKey: PublicKey): Promise<void> {
157
+ assert(PublicKey.isPublicKey(feedKey));
158
+ await this.addPartyFeed(partyKey, feedKey);
159
+ const party = this.getParty(partyKey) ?? failUndefined();
160
+ party.genesisFeedKey = feedKey;
161
+ await this._save();
162
+ }
163
+
150
164
  /**
151
165
  * Sets the data feed key in the party specified by public key and saves updated data in persistent storage.
152
166
  * Update party's feed list.
@@ -183,3 +197,11 @@ export class MetadataStore {
183
197
  await this._save();
184
198
  }
185
199
  }
200
+
201
+ const toBytesInt32 = (num: number) => {
202
+ const buf = Buffer.alloc(4);
203
+ buf.writeInt32LE(num, 0);
204
+ return buf;
205
+ };
206
+
207
+ const fromBytesInt32 = (buf: Buffer) => buf.readInt32LE(0);
@@ -2,14 +2,14 @@
2
2
  // Copyright 2021 DXOS.org
3
3
  //
4
4
 
5
- import assert from 'assert';
6
5
  import debug from 'debug';
6
+ import assert from 'node:assert';
7
7
 
8
8
  import { Event, synchronized } from '@dxos/async';
9
9
  import { Keyring, KeyType } from '@dxos/credentials';
10
- import { FeedSelector, FeedStoreIterator, MessageSelector, Timeframe } from '@dxos/echo-protocol';
10
+ import { FeedSelector, FeedStoreIterator, MessageSelector } from '@dxos/echo-protocol';
11
11
  import { FeedDescriptor, FeedStore } from '@dxos/feed-store';
12
- import { PublicKey } from '@dxos/protocols';
12
+ import { PublicKey, Timeframe } from '@dxos/protocols';
13
13
  import { ComplexMap } from '@dxos/util';
14
14
 
15
15
  import { MetadataStore } from './metadata-store';
@@ -5,16 +5,16 @@
5
5
  import expect from 'expect';
6
6
  import { it as test } from 'mocha';
7
7
 
8
- import { promiseTimeout } from '@dxos/async';
8
+ import { promiseTimeout, sleep } from '@dxos/async';
9
9
  import { createFeedAdmitMessage, createPartyGenesisMessage, Keyring, KeyType } from '@dxos/credentials';
10
10
  import { createId } from '@dxos/crypto';
11
11
  import { checkType } from '@dxos/debug';
12
- import { codec, FeedMessage, Timeframe } from '@dxos/echo-protocol';
12
+ import { codec, FeedMessage } from '@dxos/echo-protocol';
13
13
  import { FeedStore } from '@dxos/feed-store';
14
14
  import { createTestProtocolPair } from '@dxos/mesh-protocol';
15
15
  import { ModelFactory } from '@dxos/model-factory';
16
16
  import { ObjectModel } from '@dxos/object-model';
17
- import { PublicKey } from '@dxos/protocols';
17
+ import { PublicKey, Timeframe } from '@dxos/protocols';
18
18
  import { createStorage, StorageType } from '@dxos/random-access-multi-storage';
19
19
  import { afterTest } from '@dxos/testutils';
20
20
 
@@ -49,7 +49,7 @@ describe('PartyPipeline', () => {
49
49
  );
50
50
 
51
51
  const feed = await partyFeedProvider.createOrOpenWritableFeed();
52
- await party.open({ feedHints: [feed.key] });
52
+ await party.open({ genesisFeedKey: feed.key });
53
53
  afterTest(async () => party.close());
54
54
 
55
55
  // PartyGenesis (self-signed by Party).
@@ -104,7 +104,7 @@ describe('PartyPipeline', () => {
104
104
  }
105
105
 
106
106
  await party.close();
107
- await party.open({ feedHints: [feedKey] });
107
+ await party.open({ genesisFeedKey: feedKey });
108
108
 
109
109
  {
110
110
  await party.database.select().exec().update.waitFor(result => result.entities.length === 2);
@@ -132,7 +132,7 @@ describe('PartyPipeline', () => {
132
132
  expect(partyFeedProvider.getFeeds().find(k => k.key.equals(feedKey.publicKey))).toBeTruthy();
133
133
  });
134
134
 
135
- test('opens feed from hints', async () => {
135
+ test('does not open unrelated feeds', async () => {
136
136
  const storage = createStorage('', StorageType.RAM);
137
137
  const feedStore = new FeedStore(storage.directory('feed'), { valueEncoding: codec });
138
138
  afterTest(async () => feedStore.close());
@@ -158,21 +158,19 @@ describe('PartyPipeline', () => {
158
158
  PublicKey.random()
159
159
  );
160
160
 
161
- await partyFeedProvider.createOrOpenWritableFeed();
161
+ const writeFeed = await partyFeedProvider.createOrOpenWritableFeed();
162
162
 
163
- const feedOpened = feedStore.feedOpenedEvent.waitForCount(1);
164
-
165
- await party.open({ feedHints: [otherFeedKey] });
163
+ await party.open({ genesisFeedKey: writeFeed.key });
166
164
  afterTest(async () => party.close());
167
165
 
168
- await feedOpened;
166
+ // Wait for events to be processed.
167
+ await sleep(5);
169
168
 
170
- expect(partyFeedProvider.getFeeds().some(k => k.key.equals(otherFeedKey))).toEqual(true);
169
+ expect(partyFeedProvider.getFeeds().some(k => k.key.equals(otherFeedKey))).toEqual(false);
171
170
  });
172
171
 
173
172
  test('manually create item', async () => {
174
173
  const { party, partyFeedProvider } = await setup();
175
- await party.open();
176
174
 
177
175
  const feed = await partyFeedProvider.createOrOpenWritableFeed();
178
176
 
@@ -193,7 +191,6 @@ describe('PartyPipeline', () => {
193
191
 
194
192
  test('admit a second feed to the party', async () => {
195
193
  const { party, keyring, partyKey, feedStore } = await setup();
196
- await party.open();
197
194
 
198
195
  const feedKey = await keyring.createKeyRecord({ type: KeyType.FEED });
199
196
  const fullKey = keyring.getFullKey(feedKey.publicKey);
@@ -223,7 +220,6 @@ describe('PartyPipeline', () => {
223
220
 
224
221
  test('admit feed and then open it', async () => {
225
222
  const { party, keyring, partyKey, feedStore } = await setup();
226
- await party.open();
227
223
 
228
224
  const feedKey = await keyring.createKeyRecord({ type: KeyType.FEED });
229
225
  const fullKey = keyring.getFullKey(feedKey.publicKey);
@@ -270,7 +266,7 @@ describe('PartyPipeline', () => {
270
266
  expect(timeframe.isEmpty()).toBeFalsy();
271
267
 
272
268
  await party.close();
273
- await party.open({ feedHints: [feedKey], targetTimeframe: timeframe });
269
+ await party.open({ genesisFeedKey: feedKey, targetTimeframe: timeframe });
274
270
  });
275
271
 
276
272
  test('two instances replicating', async () => {
@@ -304,9 +300,7 @@ describe('PartyPipeline', () => {
304
300
  [peer1.partyKey]
305
301
  ));
306
302
 
307
- await party2.open({
308
- feedHints: [peer1.feedKey]
309
- });
303
+ await party2.open({ genesisFeedKey: peer1.feedKey });
310
304
  afterTest(async () => party2.close());
311
305
 
312
306
  createTestProtocolPair(
@@ -2,14 +2,16 @@
2
2
  // Copyright 2021 DXOS.org
3
3
  //
4
4
 
5
- import assert from 'assert';
5
+ import assert from 'node:assert';
6
6
 
7
7
  import { synchronized } from '@dxos/async';
8
8
  import { KeyType, Message as HaloMessage } from '@dxos/credentials';
9
9
  import { timed } from '@dxos/debug';
10
- import { createFeedWriter, DatabaseSnapshot, FeedSelector, FeedWriter, PartyKey, PartySnapshot, Timeframe } from '@dxos/echo-protocol';
10
+ import {
11
+ createFeedWriter, DatabaseSnapshot, FeedSelector, FeedWriter, PartyKey, PartySnapshot
12
+ } from '@dxos/echo-protocol';
11
13
  import { ModelFactory } from '@dxos/model-factory';
12
- import { PublicKey } from '@dxos/protocols';
14
+ import { PublicKey, Timeframe } from '@dxos/protocols';
13
15
  import { SubscriptionGroup } from '@dxos/util';
14
16
 
15
17
  import { createMessageSelector, PartyProcessor, PartyFeedProvider, FeedMuxer } from '.';
@@ -30,9 +32,9 @@ export interface PipelineOptions {
30
32
 
31
33
  export interface OpenOptions {
32
34
  /**
33
- * Keys of initial feeds needed to bootstrap the party.
35
+ * Initial feed that contains PartyGenesis message and acts as the root for the feed DAG.
34
36
  */
35
- feedHints?: PublicKey[]
37
+ genesisFeedKey: PublicKey
36
38
  /**
37
39
  * Timeframe to start processing feed messages from.
38
40
  */
@@ -123,9 +125,9 @@ export class PartyPipeline {
123
125
  */
124
126
  @synchronized
125
127
  @timed(1_000)
126
- async open (options: OpenOptions = {}) {
128
+ async open (options: OpenOptions) {
127
129
  const {
128
- feedHints = [],
130
+ genesisFeedKey,
129
131
  initialTimeframe,
130
132
  targetTimeframe
131
133
  } = options;
@@ -147,17 +149,16 @@ export class PartyPipeline {
147
149
  void this._feedProvider.createOrOpenReadOnlyFeed(feed);
148
150
  }));
149
151
 
150
- if (feedHints.length > 0) {
151
- await this._partyProcessor.takeHints(feedHints.map(publicKey => ({ publicKey, type: KeyType.FEED })));
152
- }
152
+ // TODO(dmaretskyi): We still need to hint at the genesis feed for some reason, not doing this breaks invitation tests.
153
+ await this._partyProcessor.takeHints([{ type: KeyType.FEED, publicKey: genesisFeedKey }]);
153
154
 
154
155
  //
155
156
  // Pipeline
156
157
  //
157
158
 
158
159
  const iterator = await this._feedProvider.createIterator(
159
- createMessageSelector(this._partyProcessor, this._timeframeClock),
160
- createFeedSelector(this._partyProcessor, feedHints),
160
+ createMessageSelector(this._timeframeClock),
161
+ createFeedSelector(this._partyProcessor, genesisFeedKey),
161
162
  initialTimeframe
162
163
  );
163
164
 
@@ -256,4 +257,4 @@ export class PartyPipeline {
256
257
  }
257
258
  }
258
259
 
259
- const createFeedSelector = (partyProcessor: PartyProcessor, hints: PublicKey[]): FeedSelector => feed => hints.some(hint => hint.equals(feed.key)) || partyProcessor.isFeedAdmitted(feed.key);
260
+ const createFeedSelector = (partyProcessor: PartyProcessor, genesisFeed: PublicKey): FeedSelector => feed => genesisFeed.equals(feed.key) || partyProcessor.isFeedAdmitted(feed.key);
@@ -2,8 +2,8 @@
2
2
  // Copyright 2020 DXOS.org
3
3
  //
4
4
 
5
- import assert from 'assert';
6
5
  import debug from 'debug';
6
+ import assert from 'node:assert';
7
7
 
8
8
  import { Event } from '@dxos/async';
9
9
  import {
@@ -5,21 +5,18 @@
5
5
  import expect from 'expect';
6
6
  import { it as test } from 'mocha';
7
7
 
8
- import { createAuthMessage, createKeyAdmitMessage, createPartyGenesisMessage, Keyring, KeyType } from '@dxos/credentials';
8
+ import { createAuthMessage, createEnvelopeMessage, createFeedAdmitMessage, createKeyAdmitMessage, createPartyGenesisMessage, Keyring, KeyType, wrapMessage } from '@dxos/credentials';
9
9
 
10
10
  import { PartyProcessor } from '../pipeline';
11
11
  import { createAuthenticator } from './authenticator';
12
- import { CredentialsSigner } from './credentials-signer';
12
+ import { createTestIdentityCredentials } from './identity-credentials';
13
13
 
14
14
  describe('authenticator', () => {
15
- // TODO(dmaretskyi): Figure out how credentials work and if this test makes sense.
16
- test.skip('authenticates admitted peer', async () => {
15
+ test('authenticates party creator', async () => {
17
16
  const keyring = new Keyring();
17
+ const identity = await createTestIdentityCredentials(keyring);
18
18
  const partyKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
19
- const identityKey = await keyring.createKeyRecord({ type: KeyType.IDENTITY });
20
- const deviceKey = await keyring.createKeyRecord({ type: KeyType.DEVICE });
21
- const feedKey = await keyring.createKeyRecord({ type: KeyType.FEED });
22
- const signer = CredentialsSigner.createDirectDeviceSigner(keyring);
19
+ const feedKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
23
20
 
24
21
  const partyProcessor = new PartyProcessor(partyKey.publicKey);
25
22
  await partyProcessor.processMessage({
@@ -27,16 +24,106 @@ describe('authenticator', () => {
27
24
  keyring,
28
25
  partyKey,
29
26
  feedKey.publicKey,
30
- identityKey
27
+ partyKey
31
28
  ),
32
29
  meta: {} as any
33
30
  });
31
+ await partyProcessor.processMessage({
32
+ data: createEnvelopeMessage(
33
+ identity.keyring,
34
+ partyKey.publicKey,
35
+ wrapMessage(identity.identityGenesis),
36
+ [partyKey]
37
+ ),
38
+ meta: {} as any
39
+ });
40
+ await partyProcessor.processMessage({
41
+ data: createFeedAdmitMessage(
42
+ keyring,
43
+ partyKey.publicKey,
44
+ feedKey.publicKey,
45
+ [identity.deviceKeyChain]
46
+ ),
47
+ meta: {} as any
48
+ });
49
+
50
+ const authenticator = createAuthenticator(partyProcessor, identity.createCredentialsSigner(), null as any);
51
+
52
+ //
53
+ // This test follows the same party creation routing as party factory.
54
+ // Oddly, it does not admit the device key to the party.
55
+ // This means that authentication is actually done using the signature created using the feed key.
56
+ //
57
+
58
+ // Does not authenticate without the feed key.
59
+ {
60
+ const credential = createAuthMessage(
61
+ keyring,
62
+ partyKey.publicKey,
63
+ identity.identityKey,
64
+ identity.deviceKey
65
+ );
66
+ expect(await authenticator.authenticate(credential.payload)).toEqual(false);
67
+ }
68
+
69
+ // Does authenticate with the feed key.
70
+ {
71
+ const credential = createAuthMessage(
72
+ keyring,
73
+ partyKey.publicKey,
74
+ identity.identityKey,
75
+ identity.deviceKey,
76
+ feedKey.publicKey
77
+ );
78
+ expect(await authenticator.authenticate(credential.payload)).toEqual(true);
79
+ }
80
+ });
81
+
82
+ test('authenticates another identity', async () => {
83
+ const keyring = new Keyring();
84
+ const identity = await createTestIdentityCredentials(keyring);
85
+ const partyKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
86
+ const feedKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
87
+
88
+ const partyProcessor = new PartyProcessor(partyKey.publicKey);
89
+ await partyProcessor.processMessage({
90
+ data: createPartyGenesisMessage(
91
+ keyring,
92
+ partyKey,
93
+ feedKey.publicKey,
94
+ partyKey
95
+ ),
96
+ meta: {} as any
97
+ });
98
+ await partyProcessor.processMessage({
99
+ data: createEnvelopeMessage(
100
+ identity.keyring,
101
+ partyKey.publicKey,
102
+ wrapMessage(identity.identityGenesis),
103
+ [partyKey]
104
+ ),
105
+ meta: {} as any
106
+ });
107
+ await partyProcessor.processMessage({
108
+ data: createFeedAdmitMessage(
109
+ keyring,
110
+ partyKey.publicKey,
111
+ feedKey.publicKey,
112
+ [identity.deviceKeyChain]
113
+ ),
114
+ meta: {} as any
115
+ });
116
+
117
+ const authenticator = createAuthenticator(partyProcessor, identity.createCredentialsSigner(), null as any);
118
+
119
+ const identity2 = await createTestIdentityCredentials(keyring);
120
+
34
121
  await partyProcessor.processMessage({
35
122
  data: createKeyAdmitMessage(
36
123
  keyring,
37
124
  partyKey.publicKey,
38
- identityKey,
39
- [partyKey, identityKey]
125
+ identity2.identityKey,
126
+ [identity.deviceKeyChain]
40
127
  ),
41
128
  meta: {} as any
42
129
  });
@@ -44,20 +131,18 @@ describe('authenticator', () => {
44
131
  data: createKeyAdmitMessage(
45
132
  keyring,
46
133
  partyKey.publicKey,
47
- deviceKey,
48
- [identityKey, deviceKey]
134
+ identity2.deviceKey,
135
+ [identity.deviceKeyChain]
49
136
  ),
50
137
  meta: {} as any
51
138
  });
52
139
 
53
- const authenticator = createAuthenticator(partyProcessor, signer, null as any);
54
140
  const credential = createAuthMessage(
55
141
  keyring,
56
142
  partyKey.publicKey,
57
- identityKey,
58
- deviceKey
143
+ identity2.identityKey,
144
+ identity2.deviceKey
59
145
  );
60
-
61
146
  expect(await authenticator.authenticate(credential.payload)).toEqual(true);
62
147
  });
63
148
  });
@@ -2,8 +2,11 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import { createIdentityInfoMessage, createKeyAdmitMessage, createPartyGenesisMessage, KeyChain, KeyRecord, Keyring, KeyType, SignedMessage } from '@dxos/credentials';
6
- import { humanize } from '@dxos/crypto';
5
+ import {
6
+ createIdentityInfoMessage, createKeyAdmitMessage, createPartyGenesisMessage,
7
+ KeyChain, KeyRecord, Keyring, KeyType, SignedMessage
8
+ } from '@dxos/credentials';
9
+ import { humanize } from '@dxos/util';
7
10
 
8
11
  import { ContactManager, Preferences } from '../halo';
9
12
  import { CredentialsSigner } from './credentials-signer';
@@ -4,7 +4,7 @@
4
4
 
5
5
  import debug from 'debug';
6
6
 
7
- import { discoveryKey, keyToString } from '@dxos/crypto';
7
+ import { discoveryKey } from '@dxos/crypto';
8
8
  import { PartyKey } from '@dxos/echo-protocol';
9
9
  import { Protocol } from '@dxos/mesh-protocol';
10
10
  import { MMSTTopology, NetworkManager, Plugin } from '@dxos/network-manager';
@@ -93,7 +93,7 @@ export class PartyProtocolFactory {
93
93
 
94
94
  userSession: {
95
95
  // TODO(burdon): See deprecated `protocolFactory` in HALO.
96
- peerId: keyToString(this._peerId.asBuffer()),
96
+ peerId: this._peerId.toHex(),
97
97
  // TODO(telackey): This ought to be the CredentialsProvider itself, so that fresh credentials can be minted.
98
98
  credentials: this._credentials.get().toString('base64')
99
99
  },
@@ -4,7 +4,6 @@
4
4
 
5
5
  import debug from 'debug';
6
6
 
7
- import { keyToString } from '@dxos/crypto';
8
7
  import { Replicator } from '@dxos/protocol-plugin-replicator';
9
8
 
10
9
  import { PartyFeedProvider } from '../pipeline';
@@ -18,7 +17,7 @@ export const createReplicatorPlugin = (feedProvider: PartyFeedProvider) =>
18
17
  new Replicator({
19
18
  load: async () => {
20
19
  const feeds = feedProvider.getFeeds();
21
- log(`Loading feeds: ${feeds.map(feed => keyToString(feed.key))}`);
20
+ log(`Loading feeds: ${feeds.map(feed => feed.key.toHex())}`);
22
21
  return feeds.map((feed) => ({ discoveryKey: feed.feed.discoveryKey }));
23
22
  },
24
23
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  import debug from 'debug';
6
6
 
7
- import { humanize } from '@dxos/crypto';
7
+ import { humanize } from '@dxos/util';
8
8
 
9
9
  import { TimeframeClock } from '../packlets/database';
10
10
  import { PartyPipeline } from '../pipeline';