@dxos/echo-db 2.33.8 → 2.33.9-dev.6b3d59af

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 (103) hide show
  1. package/dist/src/halo/halo-factory.d.ts +1 -1
  2. package/dist/src/halo/halo-factory.d.ts.map +1 -1
  3. package/dist/src/halo/halo-factory.js +7 -6
  4. package/dist/src/halo/halo-factory.js.map +1 -1
  5. package/dist/src/halo/halo-party.d.ts +4 -6
  6. package/dist/src/halo/halo-party.d.ts.map +1 -1
  7. package/dist/src/halo/halo-party.js +10 -4
  8. package/dist/src/halo/halo-party.js.map +1 -1
  9. package/dist/src/halo/identity-manager.d.ts.map +1 -1
  10. package/dist/src/halo/identity-manager.js +8 -2
  11. package/dist/src/halo/identity-manager.js.map +1 -1
  12. package/dist/src/halo/party-opener.d.ts.map +1 -1
  13. package/dist/src/halo/party-opener.js +2 -4
  14. package/dist/src/halo/party-opener.js.map +1 -1
  15. package/dist/src/halo/preferences.d.ts.map +1 -1
  16. package/dist/src/halo/preferences.js +3 -6
  17. package/dist/src/halo/preferences.js.map +1 -1
  18. package/dist/src/invitations/greeting-initiator.d.ts +5 -4
  19. package/dist/src/invitations/greeting-initiator.d.ts.map +1 -1
  20. package/dist/src/invitations/greeting-initiator.js +2 -2
  21. package/dist/src/invitations/greeting-initiator.js.map +1 -1
  22. package/dist/src/invitations/greeting-responder.d.ts +4 -7
  23. package/dist/src/invitations/greeting-responder.d.ts.map +1 -1
  24. package/dist/src/invitations/greeting-responder.js +4 -18
  25. package/dist/src/invitations/greeting-responder.js.map +1 -1
  26. package/dist/src/invitations/invitation-factory.d.ts +2 -1
  27. package/dist/src/invitations/invitation-factory.d.ts.map +1 -1
  28. package/dist/src/invitations/invitation-factory.js +3 -5
  29. package/dist/src/invitations/invitation-factory.js.map +1 -1
  30. package/dist/src/packlets/database/data-mirror.test.js +1 -1
  31. package/dist/src/packlets/database/data-mirror.test.js.map +1 -1
  32. package/dist/src/packlets/database/item-demuxer.test.js +1 -1
  33. package/dist/src/packlets/database/item-demuxer.test.js.map +1 -1
  34. package/dist/src/packlets/database/testing.js +1 -1
  35. package/dist/src/packlets/database/testing.js.map +1 -1
  36. package/dist/src/packlets/database/timeframe-clock.d.ts +2 -1
  37. package/dist/src/packlets/database/timeframe-clock.d.ts.map +1 -1
  38. package/dist/src/packlets/database/timeframe-clock.js +15 -5
  39. package/dist/src/packlets/database/timeframe-clock.js.map +1 -1
  40. package/dist/src/parties/data-party.d.ts +4 -3
  41. package/dist/src/parties/data-party.d.ts.map +1 -1
  42. package/dist/src/parties/data-party.js +9 -5
  43. package/dist/src/parties/data-party.js.map +1 -1
  44. package/dist/src/parties/data-party.test.js +12 -12
  45. package/dist/src/parties/data-party.test.js.map +1 -1
  46. package/dist/src/parties/party-factory.d.ts +2 -4
  47. package/dist/src/parties/party-factory.d.ts.map +1 -1
  48. package/dist/src/parties/party-factory.js +11 -8
  49. package/dist/src/parties/party-factory.js.map +1 -1
  50. package/dist/src/parties/party-manager.d.ts +1 -1
  51. package/dist/src/parties/party-manager.d.ts.map +1 -1
  52. package/dist/src/parties/party-manager.js +13 -13
  53. package/dist/src/parties/party-manager.js.map +1 -1
  54. package/dist/src/parties/party-manager.test.js +9 -6
  55. package/dist/src/parties/party-manager.test.js.map +1 -1
  56. package/dist/src/pipeline/feed-muxer.js +1 -1
  57. package/dist/src/pipeline/feed-muxer.js.map +1 -1
  58. package/dist/src/pipeline/feed-muxer.test.js +2 -2
  59. package/dist/src/pipeline/feed-muxer.test.js.map +1 -1
  60. package/dist/src/pipeline/message-selector.d.ts +1 -2
  61. package/dist/src/pipeline/message-selector.d.ts.map +1 -1
  62. package/dist/src/pipeline/message-selector.js +4 -31
  63. package/dist/src/pipeline/message-selector.js.map +1 -1
  64. package/dist/src/pipeline/metadata-store.d.ts +7 -2
  65. package/dist/src/pipeline/metadata-store.d.ts.map +1 -1
  66. package/dist/src/pipeline/metadata-store.js +9 -0
  67. package/dist/src/pipeline/metadata-store.js.map +1 -1
  68. package/dist/src/pipeline/party-feed-provider.d.ts +2 -2
  69. package/dist/src/pipeline/party-feed-provider.d.ts.map +1 -1
  70. package/dist/src/pipeline/party-feed-provider.js +2 -1
  71. package/dist/src/pipeline/party-feed-provider.js.map +1 -1
  72. package/dist/src/pipeline/party-pipeline.d.ts +5 -5
  73. package/dist/src/pipeline/party-pipeline.d.ts.map +1 -1
  74. package/dist/src/pipeline/party-pipeline.js +6 -7
  75. package/dist/src/pipeline/party-pipeline.js.map +1 -1
  76. package/dist/src/pipeline/party-pipeline.test.js +13 -18
  77. package/dist/src/pipeline/party-pipeline.test.js.map +1 -1
  78. package/dist/tsconfig.tsbuildinfo +1 -1
  79. package/package.json +17 -17
  80. package/src/halo/halo-factory.ts +6 -6
  81. package/src/halo/halo-party.ts +14 -6
  82. package/src/halo/identity-manager.ts +11 -2
  83. package/src/halo/party-opener.ts +2 -4
  84. package/src/halo/preferences.ts +4 -8
  85. package/src/invitations/greeting-initiator.ts +8 -2
  86. package/src/invitations/greeting-responder.ts +7 -26
  87. package/src/invitations/invitation-factory.ts +2 -2
  88. package/src/packlets/database/data-mirror.test.ts +2 -2
  89. package/src/packlets/database/item-demuxer.test.ts +2 -2
  90. package/src/packlets/database/testing.ts +2 -2
  91. package/src/packlets/database/timeframe-clock.ts +4 -1
  92. package/src/parties/data-party.test.ts +12 -12
  93. package/src/parties/data-party.ts +14 -6
  94. package/src/parties/party-factory.ts +17 -9
  95. package/src/parties/party-manager.test.ts +9 -6
  96. package/src/parties/party-manager.ts +14 -12
  97. package/src/pipeline/feed-muxer.test.ts +2 -2
  98. package/src/pipeline/feed-muxer.ts +2 -2
  99. package/src/pipeline/message-selector.ts +4 -35
  100. package/src/pipeline/metadata-store.ts +15 -2
  101. package/src/pipeline/party-feed-provider.ts +2 -2
  102. package/src/pipeline/party-pipeline.test.ts +13 -19
  103. package/src/pipeline/party-pipeline.ts +13 -12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/echo-db",
3
- "version": "2.33.8",
3
+ "version": "2.33.9-dev.6b3d59af",
4
4
  "description": "ECHO database.",
5
5
  "license": "MIT",
6
6
  "main": "dist/src/index.js",
@@ -10,22 +10,22 @@
10
10
  "src"
11
11
  ],
12
12
  "dependencies": {
13
- "@dxos/async": "2.33.8",
14
- "@dxos/codec-protobuf": "2.33.8",
15
- "@dxos/credentials": "2.33.8",
16
- "@dxos/crypto": "2.33.8",
17
- "@dxos/debug": "2.33.8",
18
- "@dxos/echo-protocol": "2.33.8",
19
- "@dxos/feed-store": "2.33.8",
20
- "@dxos/mesh-protocol": "2.33.8",
21
- "@dxos/model-factory": "2.33.8",
22
- "@dxos/network-manager": "2.33.8",
23
- "@dxos/object-model": "2.33.8",
24
- "@dxos/protocol-plugin-presence": "2.33.8",
25
- "@dxos/protocol-plugin-replicator": "2.33.8",
26
- "@dxos/protocols": "2.33.8",
27
- "@dxos/random-access-multi-storage": "2.33.8",
28
- "@dxos/util": "2.33.8",
13
+ "@dxos/async": "2.33.9-dev.6b3d59af",
14
+ "@dxos/codec-protobuf": "2.33.9-dev.6b3d59af",
15
+ "@dxos/credentials": "2.33.9-dev.6b3d59af",
16
+ "@dxos/crypto": "2.33.9-dev.6b3d59af",
17
+ "@dxos/debug": "2.33.9-dev.6b3d59af",
18
+ "@dxos/echo-protocol": "2.33.9-dev.6b3d59af",
19
+ "@dxos/feed-store": "2.33.9-dev.6b3d59af",
20
+ "@dxos/mesh-protocol": "2.33.9-dev.6b3d59af",
21
+ "@dxos/model-factory": "2.33.9-dev.6b3d59af",
22
+ "@dxos/network-manager": "2.33.9-dev.6b3d59af",
23
+ "@dxos/object-model": "2.33.9-dev.6b3d59af",
24
+ "@dxos/protocol-plugin-presence": "2.33.9-dev.6b3d59af",
25
+ "@dxos/protocol-plugin-replicator": "2.33.9-dev.6b3d59af",
26
+ "@dxos/protocols": "2.33.9-dev.6b3d59af",
27
+ "@dxos/random-access-multi-storage": "2.33.9-dev.6b3d59af",
28
+ "@dxos/util": "2.33.9-dev.6b3d59af",
29
29
  "base-x": "~3.0.9",
30
30
  "buffer-json-encoding": "^1.0.2",
31
31
  "debug": "^4.3.3",
@@ -54,17 +54,15 @@ export class HaloFactory {
54
54
  private readonly _options: PipelineOptions = {}
55
55
  ) {}
56
56
 
57
- async constructParty (feedHints: PublicKey[]): Promise<HaloParty> {
57
+ async constructParty (): Promise<HaloParty> {
58
58
  const credentialsSigner = CredentialsSigner.createDirectDeviceSigner(this._keyring);
59
59
  const feedProvider = this._feedProviderFactory(credentialsSigner.getIdentityKey().publicKey);
60
- const writableFeed = await feedProvider.createOrOpenWritableFeed();
61
60
  const halo = new HaloParty(
62
61
  this._modelFactory,
63
62
  this._snapshotStore,
64
63
  feedProvider,
65
64
  credentialsSigner,
66
65
  this._networkManager,
67
- [...feedHints, writableFeed.key],
68
66
  undefined,
69
67
  this._options
70
68
  );
@@ -81,10 +79,11 @@ export class HaloFactory {
81
79
  await this._keyring.createKeyRecord({ type: KeyType.DEVICE });
82
80
 
83
81
  // 1. Create a feed for the HALO.
84
- const halo = await this.constructParty([]);
82
+ const halo = await this.constructParty();
85
83
  const feedKey = await halo.getWriteFeedKey();
86
84
  const feedKeyPair = this._keyring.getKey(feedKey);
87
85
  assert(feedKeyPair);
86
+ halo._setGenesisFeedKey(feedKey);
88
87
 
89
88
  // Connect the pipeline.
90
89
  await halo.open();
@@ -193,9 +192,10 @@ export class HaloFactory {
193
192
  );
194
193
 
195
194
  await initiator.connect();
196
- const { hints } = await initiator.redeemInvitation(secretProvider);
195
+ const { genesisFeedKey } = await initiator.redeemInvitation(secretProvider);
197
196
 
198
- const halo = await this.constructParty(hints);
197
+ const halo = await this.constructParty();
198
+ halo._setGenesisFeedKey(genesisFeedKey);
199
199
  await halo.open();
200
200
 
201
201
  await initiator.destroy();
@@ -5,12 +5,10 @@
5
5
  import assert from 'assert';
6
6
 
7
7
  import { Event, synchronized } from '@dxos/async';
8
- import { KeyHint } from '@dxos/credentials';
9
8
  import { timed } from '@dxos/debug';
10
- import { Timeframe } from '@dxos/echo-protocol';
11
9
  import { ModelFactory } from '@dxos/model-factory';
12
10
  import { NetworkManager } from '@dxos/network-manager';
13
- import { PublicKey } from '@dxos/protocols';
11
+ import { PublicKey, Timeframe } from '@dxos/protocols';
14
12
 
15
13
  import { InvitationAuthenticator, InvitationDescriptor, InvitationFactory, InvitationOptions } from '../invitations';
16
14
  import { PARTY_ITEM_TYPE } from '../parties';
@@ -32,7 +30,7 @@ export const HALO_PARTY_DEVICE_PREFERENCES_TYPE = 'dxos:item/halo/device/prefere
32
30
  */
33
31
  export interface JoinedParty {
34
32
  partyKey: PublicKey,
35
- keyHints: KeyHint[]
33
+ genesisFeed: PublicKey,
36
34
  }
37
35
 
38
36
  /**
@@ -48,13 +46,14 @@ export class HaloParty {
48
46
  private readonly _contactManager: ContactManager;
49
47
  private readonly _preferences: Preferences;
50
48
 
49
+ private _genesisFeedKey?: PublicKey | undefined;
50
+
51
51
  constructor (
52
52
  modelFactory: ModelFactory,
53
53
  snapshotStore: SnapshotStore,
54
54
  private readonly _feedProvider: PartyFeedProvider,
55
55
  private readonly _credentialsSigner: CredentialsSigner,
56
56
  private readonly _networkManager: NetworkManager,
57
- private readonly _feedHints: PublicKey[] = [],
58
57
  private readonly _initialTimeframe: Timeframe | undefined,
59
58
  _options: PipelineOptions
60
59
  ) {
@@ -134,6 +133,13 @@ export class HaloParty {
134
133
  return this._partyCore.processor;
135
134
  }
136
135
 
136
+ /**
137
+ * @internal
138
+ */
139
+ _setGenesisFeedKey (genesisFeedKey: PublicKey) {
140
+ this._genesisFeedKey = genesisFeedKey;
141
+ }
142
+
137
143
  /**
138
144
  * Opens the pipeline and connects the streams.
139
145
  */
@@ -144,13 +150,15 @@ export class HaloParty {
144
150
  return this;
145
151
  }
146
152
 
153
+ assert(this._genesisFeedKey);
147
154
  await this._partyCore.open({
148
- feedHints: this._feedHints,
155
+ genesisFeedKey: this._genesisFeedKey,
149
156
  initialTimeframe: this._initialTimeframe
150
157
  });
151
158
 
152
159
  this._invitationManager = new InvitationFactory(
153
160
  this._partyCore.processor,
161
+ this._genesisFeedKey,
154
162
  this._credentialsSigner,
155
163
  this._partyCore.credentialsWriter,
156
164
  this._networkManager
@@ -69,9 +69,14 @@ export class IdentityManager {
69
69
  async loadFromStorage () {
70
70
  const identityKey = this.getIdentityKey();
71
71
  if (identityKey) {
72
- if (this._metadataStore.getParty(identityKey.publicKey)) {
72
+ const metadata = this._metadataStore.getParty(identityKey.publicKey);
73
+ if (metadata) {
73
74
  // TODO(marik-d): Snapshots for halo party?
74
- const halo = await this._haloFactory.constructParty([]);
75
+ const halo = await this._haloFactory.constructParty();
76
+
77
+ assert(metadata.genesisFeedKey);
78
+ halo._setGenesisFeedKey(metadata.genesisFeedKey);
79
+
75
80
  // Always open the HALO.
76
81
  await halo.open();
77
82
  await this._initialize(halo);
@@ -89,6 +94,10 @@ export class IdentityManager {
89
94
  assert(!this._identity, 'Identity already initialized.');
90
95
 
91
96
  const halo = await this._haloFactory.createHalo(options);
97
+
98
+ const identityKey = this.getIdentityKey() ?? failUndefined();
99
+ await this._metadataStore.setGenesisFeed(identityKey.publicKey, await halo.getWriteFeedKey());
100
+
92
101
  await this._initialize(halo);
93
102
  return halo;
94
103
  }
@@ -4,7 +4,6 @@
4
4
 
5
5
  import debug from 'debug';
6
6
 
7
- import { KeyType } from '@dxos/credentials';
8
7
  import { SubscriptionGroup, Unsubscribe } from '@dxos/util';
9
8
 
10
9
  import { PartyManager } from '../parties';
@@ -25,9 +24,8 @@ export const autoPartyOpener = (preferences: Preferences, partyManager: PartyMan
25
24
 
26
25
  for (const partyDesc of values) {
27
26
  if (!partyManager.parties.some(x => x.key === partyDesc.partyKey)) {
28
- log(`Auto-opening new Party from HALO: ${partyDesc.partyKey.toHex()} hints=${JSON.stringify(partyDesc.keyHints)}`);
29
- const feedHints = partyDesc.keyHints.filter(hint => hint.type === KeyType.FEED).map(hint => hint.publicKey!);
30
- await partyManager.addParty(partyDesc.partyKey, feedHints);
27
+ log(`Auto-opening new Party from HALO: ${partyDesc.partyKey.toHex()}`);
28
+ await partyManager.addParty(partyDesc.partyKey, partyDesc.genesisFeed);
31
29
  }
32
30
  }
33
31
  }));
@@ -7,7 +7,6 @@ import stableStringify from 'json-stable-stringify';
7
7
  import defaultsDeep from 'lodash.defaultsdeep';
8
8
 
9
9
  import { Event } from '@dxos/async';
10
- import { KeyHint } from '@dxos/credentials';
11
10
  import { raise } from '@dxos/debug';
12
11
  import { ObjectModel } from '@dxos/object-model';
13
12
  import { PublicKey } from '@dxos/protocols';
@@ -151,8 +150,8 @@ export class Preferences {
151
150
  type: HALO_PARTY_DESCRIPTOR_TYPE,
152
151
  props: {
153
152
  publicKey: joinedParty.partyKey.asBuffer(),
154
- subscribed: true,
155
- hints: joinedParty.keyHints.map(hint => ({ ...hint, publicKey: hint.publicKey?.toHex() }))
153
+ genesisFeed: joinedParty.genesisFeed.toHex(),
154
+ subscribed: true
156
155
  }
157
156
  });
158
157
  }
@@ -160,12 +159,9 @@ export class Preferences {
160
159
  subscribeToJoinedPartyList (callback: (parties: JoinedParty[]) => void): () => void {
161
160
  const database = this._getDatabase() ?? raise(new IdentityNotInitializedError());
162
161
 
163
- const converter = (partyDesc: Item<any>) => ({
162
+ const converter = (partyDesc: Item<any>): JoinedParty => ({
164
163
  partyKey: PublicKey.from(partyDesc.model.get('publicKey')),
165
- keyHints: Object.values(partyDesc.model.get('hints')).map((hint: any) => ({
166
- ...hint,
167
- publicKey: PublicKey.from(hint.publicKey)
168
- } as KeyHint))
164
+ genesisFeed: PublicKey.from(partyDesc.model.get('genesisFeed'))
169
165
  });
170
166
 
171
167
  const result = database.select({ type: HALO_PARTY_DESCRIPTOR_TYPE }).exec();
@@ -35,6 +35,11 @@ const log = debug('dxos:echo-db:greeting-initiator');
35
35
 
36
36
  const DEFAULT_TIMEOUT = 30_000;
37
37
 
38
+ export interface InvitationResult {
39
+ partyKey: PublicKey;
40
+ genesisFeedKey: PublicKey
41
+ }
42
+
38
43
  /**
39
44
  * Attempts to connect to a greeting responder to 'redeem' an invitation, potentially with some out-of-band
40
45
  * authentication check, in order to be admitted to a Party.
@@ -112,7 +117,7 @@ export class GreetingInitiator {
112
117
  /**
113
118
  * Called after connecting to initiate greeting protocol exchange.
114
119
  */
115
- async redeemInvitation (secretProvider: SecretProvider): Promise<{ partyKey: PublicKey, hints: PublicKey[] }> {
120
+ async redeemInvitation (secretProvider: SecretProvider): Promise<InvitationResult> {
116
121
  assert(this._state === GreetingState.CONNECTED);
117
122
  const { swarmKey } = this._invitationDescriptor;
118
123
 
@@ -170,9 +175,10 @@ export class GreetingInitiator {
170
175
  await this.disconnect();
171
176
 
172
177
  this._state = GreetingState.SUCCEEDED;
178
+ assert(notarizeResponse.genesisFeed);
173
179
  return {
174
180
  partyKey,
175
- hints: notarizeResponse.feedHints ?? []
181
+ genesisFeedKey: notarizeResponse.genesisFeed
176
182
  };
177
183
  }
178
184
 
@@ -9,12 +9,11 @@ import { Event, waitForCondition } from '@dxos/async';
9
9
  import {
10
10
  admitsKeys,
11
11
  createEnvelopeMessage, Greeter,
12
- GreetingCommandPlugin, KeyHint,
12
+ GreetingCommandPlugin,
13
13
  Keyring,
14
- KeyType,
15
14
  SecretProvider,
16
- SecretValidator
17
- , Message as HaloMessage
15
+ SecretValidator,
16
+ Message as HaloMessage
18
17
  } from '@dxos/credentials';
19
18
  import { randomBytes } from '@dxos/crypto';
20
19
  import { FeedWriter, SwarmKey } from '@dxos/echo-protocol';
@@ -60,13 +59,14 @@ export class GreetingResponder {
60
59
  constructor (
61
60
  private readonly _networkManager: NetworkManager,
62
61
  private readonly _partyProcessor: PartyStateProvider,
62
+ private readonly _genesisFeedKey: PublicKey,
63
63
  private readonly _credentialsSigner: CredentialsSigner,
64
64
  private readonly _credentialsWriter: FeedWriter<HaloMessage>
65
65
  ) {
66
66
  this._greeter = new Greeter(
67
67
  this._partyProcessor.partyKey,
68
- async (messages: any) => this._writeCredentialsToParty(messages),
69
- async () => this._gatherHints()
68
+ this._genesisFeedKey,
69
+ async (messages: any) => this._writeCredentialsToParty(messages)
70
70
  );
71
71
 
72
72
  this._greeterPlugin = new GreetingCommandPlugin(Buffer.from(this._swarmKey), this._greeter.createMessageHandler());
@@ -210,6 +210,7 @@ export class GreetingResponder {
210
210
  // Place the self-signed messages inside an Envelope, sign then write the signed Envelope to the Party.
211
211
  const envelopes = [];
212
212
  for (const message of messages) {
213
+ // TODO(dmaretskyi): Refactor to pass in a callback: `await admitKeys(messages)`.
213
214
  const admittedKeys = admitsKeys(message);
214
215
 
215
216
  // TODO(telackey): Add hasKey/isMember to PartyProcessor?
@@ -238,24 +239,4 @@ export class GreetingResponder {
238
239
  // Return the signed messages to the caller because copies are sent back to the invitee.
239
240
  return envelopes;
240
241
  }
241
-
242
- /**
243
- * Callback to gather member key and feed "hints" for the Invitee.
244
- * @private
245
- */
246
- _gatherHints (): KeyHint[] {
247
- assert(this._state === GreetingState.SUCCEEDED);
248
-
249
- const memberKeys = this._partyProcessor.memberKeys.map((publicKey) => ({
250
- publicKey,
251
- type: KeyType.UNKNOWN
252
- }));
253
-
254
- const memberFeeds = this._partyProcessor.feedKeys.map((publicKey) => ({
255
- publicKey,
256
- type: KeyType.FEED
257
- }));
258
-
259
- return [...memberKeys, ...memberFeeds];
260
- }
261
242
  }
@@ -21,8 +21,7 @@ import { InvitationDescriptor, InvitationDescriptorType } from './invitation-des
21
21
  export class InvitationFactory {
22
22
  constructor (
23
23
  private readonly _partyProcessor: PartyStateProvider,
24
- // This needs to be a provider in case this is a backend for the HALO party.
25
- // Then the identity would be changed after this is instantiated.
24
+ private readonly _genesisFeedKey: PublicKey,
26
25
  private readonly _credentialsSigner: CredentialsSigner,
27
26
  private readonly _credentialsWriter: FeedWriter<HaloMessage>,
28
27
  private readonly _networkManager: NetworkManager
@@ -62,6 +61,7 @@ export class InvitationFactory {
62
61
  const responder = new GreetingResponder(
63
62
  this._networkManager,
64
63
  this._partyProcessor,
64
+ this._genesisFeedKey,
65
65
  this._credentialsSigner,
66
66
  this._credentialsWriter
67
67
  );
@@ -6,10 +6,10 @@ import expect from 'expect';
6
6
  import { it as test } from 'mocha';
7
7
 
8
8
  import { promiseTimeout } from '@dxos/async';
9
- import { EchoEnvelope, MockFeedWriter, Timeframe } from '@dxos/echo-protocol';
9
+ import { EchoEnvelope, MockFeedWriter } from '@dxos/echo-protocol';
10
10
  import { ModelFactory } from '@dxos/model-factory';
11
11
  import { ObjectModel } from '@dxos/object-model';
12
- import { PublicKey } from '@dxos/protocols';
12
+ import { PublicKey, Timeframe } from '@dxos/protocols';
13
13
 
14
14
  import { DataMirror } from './data-mirror';
15
15
  import { DataServiceHost } from './data-service-host';
@@ -9,10 +9,10 @@ import { it as test } from 'mocha';
9
9
  import { latch } from '@dxos/async';
10
10
  import { createId } from '@dxos/crypto';
11
11
  import { checkType } from '@dxos/debug';
12
- import { EchoEnvelope, MockFeedWriter, Timeframe } from '@dxos/echo-protocol';
12
+ import { EchoEnvelope, MockFeedWriter } from '@dxos/echo-protocol';
13
13
  import { ModelFactory, TestModel } from '@dxos/model-factory';
14
14
  import { ObjectModel } from '@dxos/object-model';
15
- import { PublicKey } from '@dxos/protocols';
15
+ import { PublicKey, Timeframe } from '@dxos/protocols';
16
16
 
17
17
  import { Item } from './item';
18
18
  import { ItemDemuxer } from './item-demuxer';
@@ -2,9 +2,9 @@
2
2
  // Copyright 2021 DXOS.org
3
3
  //
4
4
 
5
- import { EchoEnvelope, MockFeedWriter, Timeframe } from '@dxos/echo-protocol';
5
+ import { EchoEnvelope, MockFeedWriter } from '@dxos/echo-protocol';
6
6
  import { ModelFactory } from '@dxos/model-factory';
7
- import { PublicKey } from '@dxos/protocols';
7
+ import { PublicKey, Timeframe } from '@dxos/protocols';
8
8
 
9
9
  import { DataServiceHost } from './data-service-host';
10
10
  import { DataServiceRouter } from './data-service-router';
@@ -3,7 +3,9 @@
3
3
  //
4
4
 
5
5
  import { Event } from '@dxos/async';
6
- import { FeedKey, Timeframe } from '@dxos/echo-protocol';
6
+ import { timed } from '@dxos/debug';
7
+ import { FeedKey } from '@dxos/echo-protocol';
8
+ import { Timeframe } from '@dxos/protocols';
7
9
 
8
10
  /**
9
11
  * Keeps state of the last timeframe that was processed by ECHO.
@@ -29,6 +31,7 @@ export class TimeframeClock {
29
31
  return !gaps.isEmpty();
30
32
  }
31
33
 
34
+ @timed(5_000)
32
35
  async waitUntilReached (target: Timeframe) {
33
36
  await this.update.waitForCondition(() => Timeframe.dependencies(target, this._timeframe).isEmpty());
34
37
  }
@@ -22,7 +22,7 @@ import { SnapshotStore } from '../snapshots';
22
22
  import { DataParty } from './data-party';
23
23
 
24
24
  describe('DataParty', () => {
25
- const createParty = async (identity: IdentityCredentials, partyKey: PublicKey, feedHints: PublicKey[]) => {
25
+ const createParty = async (identity: IdentityCredentials, partyKey: PublicKey, genesisFeedKey?: PublicKey) => {
26
26
 
27
27
  const storage = createStorage('', StorageType.RAM);
28
28
  const snapshotStore = new SnapshotStore(storage.directory('snapshots'));
@@ -43,7 +43,7 @@ describe('DataParty', () => {
43
43
  identity.preferences,
44
44
  networkManager
45
45
  );
46
- party._setFeedHints([...feedHints, writableFeed.key]);
46
+ party._setGenesisFeedKey(genesisFeedKey ?? writableFeed.key);
47
47
  return party;
48
48
  };
49
49
 
@@ -51,7 +51,7 @@ describe('DataParty', () => {
51
51
  const keyring = new Keyring();
52
52
  const identity = await createTestIdentityCredentials(keyring);
53
53
  const partyKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
54
- const party = await createParty(identity, partyKey.publicKey, []);
54
+ const party = await createParty(identity, partyKey.publicKey);
55
55
 
56
56
  await party.open();
57
57
  await party.close();
@@ -61,7 +61,7 @@ describe('DataParty', () => {
61
61
  const keyring = new Keyring();
62
62
  const identity = await createTestIdentityCredentials(keyring);
63
63
  const partyKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
64
- const party = await createParty(identity, partyKey.publicKey, []);
64
+ const party = await createParty(identity, partyKey.publicKey);
65
65
  await party.open();
66
66
 
67
67
  const feed = await party.getWriteFeed();
@@ -81,7 +81,7 @@ describe('DataParty', () => {
81
81
  const keyring = new Keyring();
82
82
  const identity = await createTestIdentityCredentials(keyring);
83
83
  const partyKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
84
- const party = await createParty(identity, partyKey.publicKey, []);
84
+ const party = await createParty(identity, partyKey.publicKey);
85
85
  await party.open();
86
86
 
87
87
  const feed = await party.getWriteFeed();
@@ -109,7 +109,7 @@ describe('DataParty', () => {
109
109
  const identity = await createTestIdentityCredentials(keyring);
110
110
  const partyKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
111
111
 
112
- const party = await createParty(identity, partyKey.publicKey, []);
112
+ const party = await createParty(identity, partyKey.publicKey);
113
113
  await party.open();
114
114
  const feed = await party.getWriteFeed();
115
115
  await party.credentialsWriter.write(createPartyGenesisMessage(
@@ -134,7 +134,7 @@ describe('DataParty', () => {
134
134
  const identityA = await createTestIdentityCredentials(keyring);
135
135
  const partyKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
136
136
 
137
- const party = await createParty(identityA, partyKey.publicKey, []);
137
+ const party = await createParty(identityA, partyKey.publicKey);
138
138
  await party.open();
139
139
  const feed = await party.getWriteFeed();
140
140
  await party.credentialsWriter.write(createPartyGenesisMessage(
@@ -161,7 +161,7 @@ describe('DataParty', () => {
161
161
  const identityA = await createTestIdentityCredentials(keyring);
162
162
  const partyKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
163
163
 
164
- const partyA = await createParty(identityA, partyKey.publicKey, []);
164
+ const partyA = await createParty(identityA, partyKey.publicKey);
165
165
  await partyA.open();
166
166
  const feedA = await partyA.getWriteFeed();
167
167
  await partyA.credentialsWriter.write(createPartyGenesisMessage(
@@ -178,7 +178,7 @@ describe('DataParty', () => {
178
178
  ));
179
179
 
180
180
  const identityB = await deriveTestDeviceCredentials(identityA);
181
- const partyB = await createParty(identityB, partyKey.publicKey, [feedA.key]);
181
+ const partyB = await createParty(identityB, partyKey.publicKey, feedA.key);
182
182
  await partyB.open();
183
183
 
184
184
  await partyA.database.createItem({ type: 'test:item-a' });
@@ -195,7 +195,7 @@ describe('DataParty', () => {
195
195
  const identityA = await createTestIdentityCredentials(new Keyring());
196
196
  const partyKeyA = await identityA.keyring.createKeyRecord({ type: KeyType.PARTY });
197
197
 
198
- const partyA = await createParty(identityA, partyKeyA.publicKey, []);
198
+ const partyA = await createParty(identityA, partyKeyA.publicKey);
199
199
  await partyA.open();
200
200
  const feedA = await partyA.getWriteFeed();
201
201
  await partyA.credentialsWriter.write(createPartyGenesisMessage(
@@ -226,9 +226,9 @@ describe('DataParty', () => {
226
226
  );
227
227
 
228
228
  await initiator.connect();
229
- const { partyKey: partyKeyB, hints: hintsB } = await initiator.redeemInvitation(defaultSecretProvider);
229
+ const { partyKey: partyKeyB, genesisFeedKey } = await initiator.redeemInvitation(defaultSecretProvider);
230
230
  expect(partyKeyB.equals(partyKeyA.publicKey));
231
- const partyB = await createParty(identityB, partyKeyB, hintsB);
231
+ const partyB = await createParty(identityB, partyKeyB, genesisFeedKey);
232
232
  await partyB.open();
233
233
  await initiator.destroy();
234
234
 
@@ -6,12 +6,12 @@ import assert from 'assert';
6
6
 
7
7
  import { synchronized, Event } from '@dxos/async';
8
8
  import { timed } from '@dxos/debug';
9
- import { PartyKey, PartySnapshot, Timeframe } from '@dxos/echo-protocol';
9
+ import { PartyKey, PartySnapshot } from '@dxos/echo-protocol';
10
10
  import { FeedDescriptor } from '@dxos/feed-store';
11
11
  import { ModelFactory } from '@dxos/model-factory';
12
12
  import { NetworkManager } from '@dxos/network-manager';
13
13
  import { ObjectModel } from '@dxos/object-model';
14
- import { PublicKey } from '@dxos/protocols';
14
+ import { PublicKey, Timeframe } from '@dxos/protocols';
15
15
 
16
16
  import { ResultSet } from '../api';
17
17
  import { ActivationOptions, PartyPreferences, Preferences } from '../halo';
@@ -46,7 +46,8 @@ export class DataParty {
46
46
  private readonly _preferences?: PartyPreferences;
47
47
  private _invitationManager?: InvitationFactory;
48
48
  private _protocol?: PartyProtocolFactory;
49
- private _feedHints: PublicKey[] = []
49
+
50
+ private _genesisFeedKey?: PublicKey | undefined;
50
51
 
51
52
  constructor (
52
53
  partyKey: PartyKey,
@@ -141,11 +142,16 @@ export class DataParty {
141
142
  await this._preferences?.setLastKnownTitle(title);
142
143
  }
143
144
 
145
+ get genesisFeedKey () {
146
+ assert(this._genesisFeedKey);
147
+ return this._genesisFeedKey;
148
+ }
149
+
144
150
  /**
145
151
  * @internal
146
152
  */
147
- _setFeedHints (feedHints: PublicKey[]) {
148
- this._feedHints = feedHints;
153
+ _setGenesisFeedKey (genesisFeedKey: PublicKey) {
154
+ this._genesisFeedKey = genesisFeedKey;
149
155
  }
150
156
 
151
157
  /**
@@ -161,8 +167,9 @@ export class DataParty {
161
167
  // TODO(dmaretskyi): May be undefined in some tests.
162
168
  const party = this._metadataStore.getParty(this._partyCore.key);
163
169
 
170
+ assert(this._genesisFeedKey);
164
171
  await this._partyCore.open({
165
- feedHints: this._feedHints,
172
+ genesisFeedKey: this._genesisFeedKey,
166
173
  initialTimeframe: this._initialTimeframe,
167
174
  targetTimeframe: party?.latestTimeframe
168
175
  });
@@ -175,6 +182,7 @@ export class DataParty {
175
182
 
176
183
  this._invitationManager = new InvitationFactory(
177
184
  this._partyCore.processor,
185
+ this._genesisFeedKey,
178
186
  this._credentialsSigner,
179
187
  this._partyCore.credentialsWriter,
180
188
  this._networkManager
@@ -14,11 +14,11 @@ import {
14
14
  wrapMessage
15
15
  } from '@dxos/credentials';
16
16
  import { failUndefined, raise, timed } from '@dxos/debug';
17
- import { createFeedWriter, FeedMessage, PartyKey, PartySnapshot, Timeframe } from '@dxos/echo-protocol';
17
+ import { createFeedWriter, FeedMessage, PartyKey, PartySnapshot } from '@dxos/echo-protocol';
18
18
  import { ModelFactory } from '@dxos/model-factory';
19
19
  import { NetworkManager } from '@dxos/network-manager';
20
20
  import { ObjectModel } from '@dxos/object-model';
21
- import { PublicKey } from '@dxos/protocols';
21
+ import { PublicKey, Timeframe } from '@dxos/protocols';
22
22
  import { humanize } from '@dxos/util';
23
23
 
24
24
  import {
@@ -59,8 +59,10 @@ export class PartyFactory {
59
59
  const party = await this.constructParty(partyKey.publicKey);
60
60
 
61
61
  const writableFeed = await party.getWriteFeed();
62
- // Hint at the newly created writable feed so that we can start replicating from it.
63
- party._setFeedHints([writableFeed.key]);
62
+ party._setGenesisFeedKey(writableFeed.key);
63
+
64
+ await this._metadataStore.addParty(partyKey.publicKey);
65
+ await this._metadataStore.setGenesisFeed(partyKey.publicKey, writableFeed.key);
64
66
 
65
67
  // Connect the pipeline.
66
68
  await party.open();
@@ -111,8 +113,6 @@ export class PartyFactory {
111
113
 
112
114
  /**
113
115
  * Constructs a party object from an existing set of feeds.
114
- * @param partyKey
115
- * @param hints
116
116
  */
117
117
  async constructParty (partyKey: PartyKey, initialTimeframe?: Timeframe) {
118
118
  const identity = this._identityProvider() ?? raise(new IdentityNotInitializedError());
@@ -172,9 +172,14 @@ export class PartyFactory {
172
172
  );
173
173
 
174
174
  await initiator.connect();
175
- const { partyKey, hints } = await initiator.redeemInvitation(secretProvider);
175
+ const { partyKey, genesisFeedKey } = await initiator.redeemInvitation(secretProvider);
176
176
  const party = await this.constructParty(partyKey);
177
- party._setFeedHints(hints);
177
+
178
+ await this._metadataStore.addParty(partyKey);
179
+ await this._metadataStore.setGenesisFeed(partyKey, genesisFeedKey);
180
+
181
+ party._setGenesisFeedKey(genesisFeedKey);
182
+
178
183
  await party.open();
179
184
  await initiator.destroy();
180
185
 
@@ -201,7 +206,10 @@ export class PartyFactory {
201
206
 
202
207
  const writableFeed = await party.getWriteFeed();
203
208
  // Hint at the newly created writable feed so that we can start replicating from it.
204
- party._setFeedHints([writableFeed.key]);
209
+ party._setGenesisFeedKey(writableFeed.key);
210
+
211
+ await this._metadataStore.addParty(partyKey.publicKey);
212
+ await this._metadataStore.setGenesisFeed(partyKey.publicKey, writableFeed.key);
205
213
 
206
214
  // Connect the pipeline.
207
215
  await party.open();