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

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 (141) hide show
  1. package/dist/src/echo.test.js +10 -10
  2. package/dist/src/echo.test.js.map +1 -1
  3. package/dist/src/halo/halo-factory.d.ts +1 -1
  4. package/dist/src/halo/halo-factory.d.ts.map +1 -1
  5. package/dist/src/halo/halo-factory.js +8 -8
  6. package/dist/src/halo/halo-factory.js.map +1 -1
  7. package/dist/src/halo/halo-party.d.ts +4 -6
  8. package/dist/src/halo/halo-party.d.ts.map +1 -1
  9. package/dist/src/halo/halo-party.js +10 -4
  10. package/dist/src/halo/halo-party.js.map +1 -1
  11. package/dist/src/halo/halo.d.ts.map +1 -1
  12. package/dist/src/halo/halo.js +2 -1
  13. package/dist/src/halo/halo.js.map +1 -1
  14. package/dist/src/halo/identity-manager.d.ts.map +1 -1
  15. package/dist/src/halo/identity-manager.js +8 -2
  16. package/dist/src/halo/identity-manager.js.map +1 -1
  17. package/dist/src/halo/party-opener.d.ts.map +1 -1
  18. package/dist/src/halo/party-opener.js +2 -4
  19. package/dist/src/halo/party-opener.js.map +1 -1
  20. package/dist/src/halo/preferences.d.ts.map +1 -1
  21. package/dist/src/halo/preferences.js +3 -6
  22. package/dist/src/halo/preferences.js.map +1 -1
  23. package/dist/src/invitations/greeting-initiator.d.ts +5 -4
  24. package/dist/src/invitations/greeting-initiator.d.ts.map +1 -1
  25. package/dist/src/invitations/greeting-initiator.js +4 -5
  26. package/dist/src/invitations/greeting-initiator.js.map +1 -1
  27. package/dist/src/invitations/greeting-protocol-provider.js +2 -2
  28. package/dist/src/invitations/greeting-protocol-provider.js.map +1 -1
  29. package/dist/src/invitations/greeting-responder.d.ts +4 -7
  30. package/dist/src/invitations/greeting-responder.d.ts.map +1 -1
  31. package/dist/src/invitations/greeting-responder.js +7 -21
  32. package/dist/src/invitations/greeting-responder.js.map +1 -1
  33. package/dist/src/invitations/halo-recovery-initiator.js +2 -2
  34. package/dist/src/invitations/halo-recovery-initiator.js.map +1 -1
  35. package/dist/src/invitations/invitation-descriptor.js +3 -3
  36. package/dist/src/invitations/invitation-descriptor.js.map +1 -1
  37. package/dist/src/invitations/invitation-factory.d.ts +2 -1
  38. package/dist/src/invitations/invitation-factory.d.ts.map +1 -1
  39. package/dist/src/invitations/invitation-factory.js +3 -5
  40. package/dist/src/invitations/invitation-factory.js.map +1 -1
  41. package/dist/src/invitations/offline-invitation-claimer.js +2 -2
  42. package/dist/src/invitations/offline-invitation-claimer.js.map +1 -1
  43. package/dist/src/packlets/database/data-mirror.test.js +1 -1
  44. package/dist/src/packlets/database/data-mirror.test.js.map +1 -1
  45. package/dist/src/packlets/database/item-demuxer.test.js +1 -1
  46. package/dist/src/packlets/database/item-demuxer.test.js.map +1 -1
  47. package/dist/src/packlets/database/testing.js +1 -1
  48. package/dist/src/packlets/database/testing.js.map +1 -1
  49. package/dist/src/packlets/database/timeframe-clock.d.ts +2 -1
  50. package/dist/src/packlets/database/timeframe-clock.d.ts.map +1 -1
  51. package/dist/src/packlets/database/timeframe-clock.js +15 -5
  52. package/dist/src/packlets/database/timeframe-clock.js.map +1 -1
  53. package/dist/src/parties/data-party.d.ts +4 -3
  54. package/dist/src/parties/data-party.d.ts.map +1 -1
  55. package/dist/src/parties/data-party.js +9 -5
  56. package/dist/src/parties/data-party.js.map +1 -1
  57. package/dist/src/parties/data-party.test.js +12 -12
  58. package/dist/src/parties/data-party.test.js.map +1 -1
  59. package/dist/src/parties/party-factory.d.ts +2 -4
  60. package/dist/src/parties/party-factory.d.ts.map +1 -1
  61. package/dist/src/parties/party-factory.js +14 -11
  62. package/dist/src/parties/party-factory.js.map +1 -1
  63. package/dist/src/parties/party-manager.d.ts +1 -1
  64. package/dist/src/parties/party-manager.d.ts.map +1 -1
  65. package/dist/src/parties/party-manager.js +13 -13
  66. package/dist/src/parties/party-manager.js.map +1 -1
  67. package/dist/src/parties/party-manager.test.js +16 -12
  68. package/dist/src/parties/party-manager.test.js.map +1 -1
  69. package/dist/src/pipeline/feed-muxer.d.ts.map +1 -1
  70. package/dist/src/pipeline/feed-muxer.js +3 -3
  71. package/dist/src/pipeline/feed-muxer.js.map +1 -1
  72. package/dist/src/pipeline/feed-muxer.test.js +2 -2
  73. package/dist/src/pipeline/feed-muxer.test.js.map +1 -1
  74. package/dist/src/pipeline/message-selector.d.ts +1 -2
  75. package/dist/src/pipeline/message-selector.d.ts.map +1 -1
  76. package/dist/src/pipeline/message-selector.js +4 -31
  77. package/dist/src/pipeline/message-selector.js.map +1 -1
  78. package/dist/src/pipeline/metadata-store.d.ts +7 -2
  79. package/dist/src/pipeline/metadata-store.d.ts.map +1 -1
  80. package/dist/src/pipeline/metadata-store.js +9 -0
  81. package/dist/src/pipeline/metadata-store.js.map +1 -1
  82. package/dist/src/pipeline/party-feed-provider.d.ts +2 -2
  83. package/dist/src/pipeline/party-feed-provider.d.ts.map +1 -1
  84. package/dist/src/pipeline/party-feed-provider.js +2 -1
  85. package/dist/src/pipeline/party-feed-provider.js.map +1 -1
  86. package/dist/src/pipeline/party-pipeline.d.ts +5 -5
  87. package/dist/src/pipeline/party-pipeline.d.ts.map +1 -1
  88. package/dist/src/pipeline/party-pipeline.js +6 -7
  89. package/dist/src/pipeline/party-pipeline.js.map +1 -1
  90. package/dist/src/pipeline/party-pipeline.test.js +13 -18
  91. package/dist/src/pipeline/party-pipeline.test.js.map +1 -1
  92. package/dist/src/protocol/identity-credentials.d.ts.map +1 -1
  93. package/dist/src/protocol/identity-credentials.js +2 -2
  94. package/dist/src/protocol/identity-credentials.js.map +1 -1
  95. package/dist/src/protocol/party-protocol-factory.js +1 -1
  96. package/dist/src/protocol/party-protocol-factory.js.map +1 -1
  97. package/dist/src/protocol/replicator-plugin.d.ts.map +1 -1
  98. package/dist/src/protocol/replicator-plugin.js +1 -2
  99. package/dist/src/protocol/replicator-plugin.js.map +1 -1
  100. package/dist/src/snapshots/snapshot-generator.js +2 -2
  101. package/dist/src/snapshots/snapshot-generator.js.map +1 -1
  102. package/dist/src/snapshots/snapshot-store.d.ts.map +1 -1
  103. package/dist/src/snapshots/snapshot-store.js +2 -2
  104. package/dist/src/snapshots/snapshot-store.js.map +1 -1
  105. package/dist/tsconfig.tsbuildinfo +1 -1
  106. package/package.json +20 -20
  107. package/src/echo.test.ts +1 -1
  108. package/src/halo/halo-factory.ts +7 -8
  109. package/src/halo/halo-party.ts +14 -6
  110. package/src/halo/halo.ts +3 -1
  111. package/src/halo/identity-manager.ts +11 -2
  112. package/src/halo/party-opener.ts +2 -4
  113. package/src/halo/preferences.ts +4 -8
  114. package/src/invitations/greeting-initiator.ts +10 -5
  115. package/src/invitations/greeting-protocol-provider.ts +2 -2
  116. package/src/invitations/greeting-responder.ts +11 -30
  117. package/src/invitations/halo-recovery-initiator.ts +3 -3
  118. package/src/invitations/invitation-descriptor.ts +5 -5
  119. package/src/invitations/invitation-factory.ts +2 -2
  120. package/src/invitations/offline-invitation-claimer.ts +3 -3
  121. package/src/packlets/database/data-mirror.test.ts +2 -2
  122. package/src/packlets/database/item-demuxer.test.ts +2 -2
  123. package/src/packlets/database/testing.ts +2 -2
  124. package/src/packlets/database/timeframe-clock.ts +4 -1
  125. package/src/parties/data-party.test.ts +12 -12
  126. package/src/parties/data-party.ts +14 -6
  127. package/src/parties/party-factory.ts +20 -12
  128. package/src/parties/party-manager.test.ts +12 -8
  129. package/src/parties/party-manager.ts +14 -12
  130. package/src/pipeline/feed-muxer.test.ts +2 -2
  131. package/src/pipeline/feed-muxer.ts +4 -4
  132. package/src/pipeline/message-selector.ts +4 -35
  133. package/src/pipeline/metadata-store.ts +15 -2
  134. package/src/pipeline/party-feed-provider.ts +2 -2
  135. package/src/pipeline/party-pipeline.test.ts +13 -19
  136. package/src/pipeline/party-pipeline.ts +13 -12
  137. package/src/protocol/identity-credentials.ts +5 -2
  138. package/src/protocol/party-protocol-factory.ts +2 -2
  139. package/src/protocol/replicator-plugin.ts +1 -2
  140. package/src/snapshots/snapshot-generator.ts +1 -1
  141. package/src/snapshots/snapshot-store.ts +2 -2
@@ -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
@@ -13,13 +13,13 @@ import {
13
13
  SecretProvider,
14
14
  wrapMessage
15
15
  } from '@dxos/credentials';
16
- import { humanize, keyToString } from '@dxos/crypto';
17
16
  import { failUndefined, raise, timed } from '@dxos/debug';
18
- import { createFeedWriter, FeedMessage, PartyKey, PartySnapshot, Timeframe } from '@dxos/echo-protocol';
17
+ import { createFeedWriter, FeedMessage, PartyKey, PartySnapshot } from '@dxos/echo-protocol';
19
18
  import { ModelFactory } from '@dxos/model-factory';
20
19
  import { NetworkManager } from '@dxos/network-manager';
21
20
  import { ObjectModel } from '@dxos/object-model';
22
- import { PublicKey } from '@dxos/protocols';
21
+ import { PublicKey, Timeframe } from '@dxos/protocols';
22
+ import { humanize } from '@dxos/util';
23
23
 
24
24
  import {
25
25
  createDataPartyAdmissionMessages,
@@ -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());
@@ -154,8 +154,8 @@ export class PartyFactory {
154
154
  const invitationClaimer = new OfflineInvitationClaimer(this._networkManager, invitationDescriptor);
155
155
  await invitationClaimer.connect();
156
156
  invitationDescriptor = await invitationClaimer.claim();
157
- log(`Party invitation ${keyToString(originalInvitation.invitation)} triggered interactive Greeting`,
158
- `at ${keyToString(invitationDescriptor.invitation)}`);
157
+ log(`Party invitation ${PublicKey.stringify(originalInvitation.invitation)} triggered interactive Greeting`,
158
+ `at ${PublicKey.stringify(invitationDescriptor.invitation)}`);
159
159
  await invitationClaimer.destroy();
160
160
  }
161
161
 
@@ -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();
@@ -19,20 +19,21 @@ import {
19
19
  } from '@dxos/credentials';
20
20
  import {
21
21
  createKeyPair,
22
- humanize,
23
22
  randomBytes,
24
23
  sign,
25
- SIGNATURE_LENGTH, verify
24
+ SIGNATURE_LENGTH,
25
+ verify
26
26
  } from '@dxos/crypto';
27
27
  import { checkType } from '@dxos/debug';
28
- import { codec, EchoEnvelope, Timeframe } from '@dxos/echo-protocol';
28
+ import { codec, EchoEnvelope } from '@dxos/echo-protocol';
29
29
  import { createWritableFeedStream, FeedStore } from '@dxos/feed-store';
30
30
  import { ModelFactory } from '@dxos/model-factory';
31
31
  import { NetworkManager } from '@dxos/network-manager';
32
32
  import { ObjectModel } from '@dxos/object-model';
33
- import { PublicKey } from '@dxos/protocols';
33
+ import { PublicKey, Timeframe } from '@dxos/protocols';
34
34
  import { createStorage, StorageType } from '@dxos/random-access-multi-storage';
35
35
  import { afterTest, testTimeout } from '@dxos/testutils';
36
+ import { humanize } from '@dxos/util';
36
37
 
37
38
  import { defaultInvitationAuthenticator, OfflineInvitationClaimer } from '../invitations';
38
39
  import { Item } from '../packlets/database';
@@ -133,17 +134,19 @@ describe('Party manager', () => {
133
134
 
134
135
  // TODO(burdon): Create multiple feeds.
135
136
  const { publicKey, secretKey } = createKeyPair();
136
- const { feed } = await feedStore.openReadWriteFeed(PublicKey.from(publicKey), secretKey);
137
+ const feed = await feedStore.openReadWriteFeed(PublicKey.from(publicKey), secretKey);
137
138
  const feedKey = await keyring.addKeyRecord({
138
139
  publicKey: PublicKey.from(feed.key),
139
140
  secretKey: feed.secretKey,
140
141
  type: KeyType.FEED
141
142
  });
142
143
 
143
- const feedStream = createWritableFeedStream(feed);
144
- feedStream.write(createPartyGenesisMessage(keyring, partyKey, feedKey.publicKey, identityKey));
144
+ await feed.append({
145
+ timeframe: new Timeframe(),
146
+ halo: createPartyGenesisMessage(keyring, partyKey, feedKey.publicKey, identityKey)
147
+ });
145
148
 
146
- await partyManager.addParty(partyKey.publicKey, [PublicKey.from(feed.key)]);
149
+ await partyManager.addParty(partyKey.publicKey, feed.key);
147
150
 
148
151
  await update;
149
152
  });
@@ -184,6 +187,7 @@ describe('Party manager', () => {
184
187
  assert(keyRecord, 'Key is not found in keyring');
185
188
  assert(keyRecord.secretKey, 'Missing secret key');
186
189
  await metadataStore.addPartyFeed(partyKey.publicKey, keyRecord.publicKey);
190
+ await metadataStore.setGenesisFeed(partyKey.publicKey, keyRecord.publicKey);
187
191
 
188
192
  // TODO(burdon): Create multiple feeds.
189
193
  const { feed } = await feedStore.openReadWriteFeed(keyRecord.publicKey, keyRecord.secretKey);
@@ -7,7 +7,7 @@ import debug from 'debug';
7
7
  import unionWith from 'lodash.unionwith';
8
8
 
9
9
  import { Event, synchronized } from '@dxos/async';
10
- import { KeyHint, KeyType, SecretProvider } from '@dxos/credentials';
10
+ import { SecretProvider } from '@dxos/credentials';
11
11
  import { failUndefined, timed } from '@dxos/debug';
12
12
  import { PartyKey, PartySnapshot } from '@dxos/echo-protocol';
13
13
  import { PublicKey } from '@dxos/protocols';
@@ -96,11 +96,15 @@ export class PartyManager {
96
96
  const snapshot = await this._snapshotStore.load(partyKey);
97
97
 
98
98
  const metadata = this._metadataStore.getParty(partyKey) ?? failUndefined();
99
+ if (!metadata.genesisFeedKey) {
100
+ log(`Skipping loading party with missing genesis feed key: ${partyKey}`);
101
+ continue;
102
+ }
99
103
 
100
104
  const party = snapshot
101
105
  ? await this._partyFactory.constructPartyFromSnapshot(snapshot)
102
106
  : await this._partyFactory.constructParty(partyKey);
103
- party._setFeedHints(metadata.feedKeys ?? []);
107
+ party._setGenesisFeedKey(metadata.genesisFeedKey);
104
108
 
105
109
  const isActive = identity?.preferences?.isPartyActive(partyKey) ?? true;
106
110
  if (isActive) {
@@ -163,7 +167,7 @@ export class PartyManager {
163
167
  * Construct a party object and start replicating with the remote peer that created that party.
164
168
  */
165
169
  @synchronized
166
- async addParty (partyKey: PartyKey, feedHints: PublicKey[] = []) {
170
+ async addParty (partyKey: PartyKey, genesisFeedKey: PublicKey) {
167
171
  assert(this._open, 'PartyManager is not open.');
168
172
 
169
173
  /*
@@ -176,11 +180,14 @@ export class PartyManager {
176
180
  return this._parties.get(partyKey);
177
181
  }
178
182
 
179
- log(`Adding party partyKey=${partyKey.toHex()} hints=${feedHints.length}`);
183
+ log(`Adding party partyKey=${partyKey.toHex()}`);
180
184
  const party = await this._partyFactory.constructParty(partyKey);
181
- party._setFeedHints(feedHints);
182
- await party.open();
185
+ party._setGenesisFeedKey(genesisFeedKey);
186
+
183
187
  await this._metadataStore.addParty(party.key);
188
+ await this._metadataStore.setGenesisFeed(party.key, genesisFeedKey);
189
+
190
+ await party.open();
184
191
  this._setParty(party);
185
192
  return party;
186
193
  }
@@ -327,14 +334,9 @@ export class PartyManager {
327
334
  return;
328
335
  }
329
336
 
330
- const keyHints: KeyHint[] = [
331
- ...party.processor.memberKeys.map(publicKey => ({ publicKey: publicKey, type: KeyType.UNKNOWN })),
332
- ...party.processor.feedKeys.map(publicKey => ({ publicKey: publicKey, type: KeyType.FEED }))
333
- ];
334
-
335
337
  await identity.preferences.recordPartyJoining({
336
338
  partyKey: party.key,
337
- keyHints
339
+ genesisFeed: party.genesisFeedKey
338
340
  });
339
341
  }
340
342
  }
@@ -9,10 +9,10 @@ import { it as test } from 'mocha';
9
9
  import { waitForCondition, latch } from '@dxos/async';
10
10
  import { createPartyGenesisMessage, Keyring, KeyType } from '@dxos/credentials';
11
11
  import { createId, createKeyPair } from '@dxos/crypto';
12
- import { codec, createFeedWriter, FeedSelector, FeedStoreIterator, IEchoStream, Timeframe } from '@dxos/echo-protocol';
12
+ import { codec, createFeedWriter, FeedSelector, FeedStoreIterator, IEchoStream } from '@dxos/echo-protocol';
13
13
  import { FeedStore, createWritableFeedStream } from '@dxos/feed-store';
14
14
  import { createSetPropertyMutation } from '@dxos/model-factory';
15
- import { PublicKey } from '@dxos/protocols';
15
+ import { PublicKey, Timeframe } from '@dxos/protocols';
16
16
  import { createStorage, StorageType } from '@dxos/random-access-multi-storage';
17
17
  import { jsonReplacer } from '@dxos/util';
18
18
 
@@ -7,12 +7,11 @@ import debug from 'debug';
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;
@@ -5,12 +5,9 @@
5
5
  import assert from 'assert';
6
6
  import debug from 'debug';
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
  };
@@ -7,8 +7,8 @@ import debug from 'debug';
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,6 +20,11 @@ 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
+
23
28
  export class MetadataStore {
24
29
  private _metadata: EchoMetadata = {
25
30
  version: STORAGE_VERSION,
@@ -147,6 +152,14 @@ export class MetadataStore {
147
152
  await this._save();
148
153
  }
149
154
 
155
+ async setGenesisFeed (partyKey: PublicKey, feedKey: PublicKey): Promise<void> {
156
+ assert(PublicKey.isPublicKey(feedKey));
157
+ await this.addPartyFeed(partyKey, feedKey);
158
+ const party = this.getParty(partyKey) ?? failUndefined();
159
+ party.genesisFeedKey = feedKey;
160
+ await this._save();
161
+ }
162
+
150
163
  /**
151
164
  * Sets the data feed key in the party specified by public key and saves updated data in persistent storage.
152
165
  * Update party's feed list.
@@ -7,9 +7,9 @@ import debug from 'debug';
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(