@dxos/echo-db 2.33.3-dev.138ca606 → 2.33.3-dev.63b42961

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 (85) hide show
  1. package/dist/src/echo.js +2 -2
  2. package/dist/src/echo.js.map +1 -1
  3. package/dist/src/echo.test.js +10 -10
  4. package/dist/src/echo.test.js.map +1 -1
  5. package/dist/src/halo/halo-party.d.ts.map +1 -1
  6. package/dist/src/halo/halo-party.js +1 -1
  7. package/dist/src/halo/halo-party.js.map +1 -1
  8. package/dist/src/halo/halo.d.ts +1 -0
  9. package/dist/src/halo/halo.d.ts.map +1 -1
  10. package/dist/src/halo/halo.js +10 -1
  11. package/dist/src/halo/halo.js.map +1 -1
  12. package/dist/src/halo/identity-manager.d.ts +0 -1
  13. package/dist/src/halo/identity-manager.d.ts.map +1 -1
  14. package/dist/src/halo/identity-manager.js +11 -11
  15. package/dist/src/halo/identity-manager.js.map +1 -1
  16. package/dist/src/halo/identity.d.ts +18 -13
  17. package/dist/src/halo/identity.d.ts.map +1 -1
  18. package/dist/src/halo/identity.js +24 -27
  19. package/dist/src/halo/identity.js.map +1 -1
  20. package/dist/src/invitations/offline-invitation-claimer.d.ts +2 -2
  21. package/dist/src/invitations/offline-invitation-claimer.d.ts.map +1 -1
  22. package/dist/src/invitations/offline-invitation-claimer.js +2 -4
  23. package/dist/src/invitations/offline-invitation-claimer.js.map +1 -1
  24. package/dist/src/parties/data-party.d.ts.map +1 -1
  25. package/dist/src/parties/data-party.js +1 -1
  26. package/dist/src/parties/data-party.js.map +1 -1
  27. package/dist/src/parties/data-party.test.d.ts +2 -0
  28. package/dist/src/parties/data-party.test.d.ts.map +1 -0
  29. package/dist/src/parties/data-party.test.js +129 -0
  30. package/dist/src/parties/data-party.test.js.map +1 -0
  31. package/dist/src/parties/party-factory.d.ts +2 -2
  32. package/dist/src/parties/party-factory.d.ts.map +1 -1
  33. package/dist/src/parties/party-factory.js +12 -20
  34. package/dist/src/parties/party-factory.js.map +1 -1
  35. package/dist/src/parties/party-manager.d.ts +2 -2
  36. package/dist/src/parties/party-manager.d.ts.map +1 -1
  37. package/dist/src/parties/party-manager.js +2 -1
  38. package/dist/src/parties/party-manager.js.map +1 -1
  39. package/dist/src/parties/party-manager.test.js +7 -22
  40. package/dist/src/parties/party-manager.test.js.map +1 -1
  41. package/dist/src/pipeline/metadata-store.d.ts.map +1 -1
  42. package/dist/src/pipeline/metadata-store.js +5 -6
  43. package/dist/src/pipeline/metadata-store.js.map +1 -1
  44. package/dist/src/pipeline/party-core.test.js +1 -1
  45. package/dist/src/pipeline/party-core.test.js.map +1 -1
  46. package/dist/src/pipeline/party-processor.d.ts +0 -5
  47. package/dist/src/pipeline/party-processor.d.ts.map +1 -1
  48. package/dist/src/pipeline/party-processor.js +0 -7
  49. package/dist/src/pipeline/party-processor.js.map +1 -1
  50. package/dist/src/protocol/credentials-signer.d.ts +4 -4
  51. package/dist/src/protocol/credentials-signer.d.ts.map +1 -1
  52. package/dist/src/protocol/credentials-signer.js +8 -8
  53. package/dist/src/protocol/credentials-signer.js.map +1 -1
  54. package/dist/src/protocol/identity-credentials.d.ts +22 -0
  55. package/dist/src/protocol/identity-credentials.d.ts.map +1 -0
  56. package/dist/src/protocol/identity-credentials.js +50 -0
  57. package/dist/src/protocol/identity-credentials.js.map +1 -0
  58. package/dist/src/protocol/party-protocol-factory.d.ts +3 -4
  59. package/dist/src/protocol/party-protocol-factory.d.ts.map +1 -1
  60. package/dist/src/protocol/party-protocol-factory.js +12 -15
  61. package/dist/src/protocol/party-protocol-factory.js.map +1 -1
  62. package/dist/src/snapshots/snapshot-store.d.ts.map +1 -1
  63. package/dist/src/snapshots/snapshot-store.js +5 -6
  64. package/dist/src/snapshots/snapshot-store.js.map +1 -1
  65. package/dist/tsconfig.tsbuildinfo +1 -1
  66. package/package.json +17 -17
  67. package/src/echo.test.ts +10 -10
  68. package/src/echo.ts +1 -1
  69. package/src/halo/halo-party.ts +1 -2
  70. package/src/halo/halo.ts +8 -1
  71. package/src/halo/identity-manager.ts +13 -14
  72. package/src/halo/identity.ts +39 -41
  73. package/src/invitations/offline-invitation-claimer.ts +6 -7
  74. package/src/parties/data-party.test.ts +212 -0
  75. package/src/parties/data-party.ts +1 -2
  76. package/src/parties/party-factory.ts +14 -20
  77. package/src/parties/party-manager.test.ts +8 -42
  78. package/src/parties/party-manager.ts +4 -3
  79. package/src/pipeline/metadata-store.ts +5 -6
  80. package/src/pipeline/party-core.test.ts +2 -4
  81. package/src/pipeline/party-processor.ts +0 -13
  82. package/src/protocol/credentials-signer.ts +9 -9
  83. package/src/protocol/identity-credentials.ts +78 -0
  84. package/src/protocol/party-protocol-factory.ts +13 -17
  85. package/src/snapshots/snapshot-store.ts +5 -6
package/src/halo/halo.ts CHANGED
@@ -46,6 +46,8 @@ export class HALO {
46
46
  private readonly _keyring: Keyring;
47
47
  private readonly _identityManager: IdentityManager;
48
48
 
49
+ private _isOpen = false;
50
+
49
51
  constructor ({
50
52
  keyring,
51
53
  networkManager,
@@ -85,7 +87,7 @@ export class HALO {
85
87
  * Whether the current identity manager has been initialized.
86
88
  */
87
89
  get isInitialized (): boolean {
88
- return this._identityManager.initialized;
90
+ return this._isOpen;
89
91
  }
90
92
 
91
93
  /**
@@ -129,6 +131,7 @@ export class HALO {
129
131
  *
130
132
  * Loads the saved identity from disk. Is called by client.
131
133
  */
134
+ @synchronized
132
135
  async open (onProgressCallback?: ((progress: OpenProgress) => void) | undefined) {
133
136
  // TODO(burdon): Replace with events.
134
137
  onProgressCallback?.({ haloOpened: false });
@@ -137,12 +140,16 @@ export class HALO {
137
140
  await this._identityManager.loadFromStorage();
138
141
 
139
142
  onProgressCallback?.({ haloOpened: true });
143
+
144
+ this._isOpen = true;
140
145
  }
141
146
 
142
147
  /**
143
148
  * Closes HALO. Automatically called when client is destroyed.
144
149
  */
150
+ @synchronized
145
151
  async close () {
152
+ this._isOpen = false;
146
153
  await this._identityManager.close();
147
154
  }
148
155
 
@@ -7,6 +7,7 @@ import debug from 'debug';
7
7
 
8
8
  import { Event, synchronized, waitForCondition } from '@dxos/async';
9
9
  import { Filter, KeyRecord, Keyring, KeyType, SecretProvider } from '@dxos/credentials';
10
+ import { failUndefined } from '@dxos/debug';
10
11
 
11
12
  import { InvitationDescriptor } from '../invitations';
12
13
  import { MetadataStore } from '../pipeline';
@@ -34,25 +35,23 @@ export class IdentityManager {
34
35
  return this._identity;
35
36
  }
36
37
 
37
- get initialized (): boolean {
38
- return this._identity !== undefined &&
39
- !!this._identity.halo &&
40
- this._identity.halo.isOpen &&
41
- !!this._identity.halo!.memberKeys.length &&
42
- !!this._identity.halo!.identityGenesis &&
43
- !!this._identity.deviceKeyChain;
44
- }
45
-
46
38
  private async _initialize (halo: HaloParty) {
47
39
  assert(halo.isOpen, 'HALO must be open.');
48
40
 
49
- this._identity = new Identity(this._keyring, halo);
50
-
51
- // Wait for the minimum set of keys and messages we need for proper function.
52
- await waitForCondition(() => this.initialized);
41
+ // Wait for the minimum set of keys and messages we need for proper function:
42
+ //
43
+ // - KeyAdmit message for the current device so we can build the device KeyChain.
44
+ // - Identity genesis so it can be copied into newly joined parties.
45
+ //
46
+ const deviceKey = this._keyring.findKey(Keyring.signingFilter({ type: KeyType.DEVICE })) ?? failUndefined();
47
+ await waitForCondition(() =>
48
+ halo.processor.isMemberKey(deviceKey.publicKey) &&
49
+ halo.identityGenesis
50
+ );
53
51
 
54
- log('HALO initialized.');
52
+ this._identity = new Identity(this._keyring, halo);
55
53
  this.ready.emit();
54
+ log('HALO initialized.');
56
55
  }
57
56
 
58
57
  async close () {
@@ -2,13 +2,14 @@
2
2
  // Copyright 2021 DXOS.org
3
3
  //
4
4
 
5
+ import assert from 'assert';
5
6
  import debug from 'debug';
6
7
 
7
- import { Filter, KeyChain, KeyRecord, Keyring, KeyType, Signer } from '@dxos/credentials';
8
- import { raise } from '@dxos/debug';
8
+ import { Filter, KeyChain, KeyRecord, Keyring, KeyType, SignedMessage, Signer } from '@dxos/credentials';
9
+ import { failUndefined } from '@dxos/debug';
9
10
 
10
- import { IdentityNotInitializedError } from '../errors';
11
11
  import { CredentialsSigner } from '../protocol/credentials-signer';
12
+ import { IdentityCredentials } from '../protocol/identity-credentials';
12
13
  import { ContactManager } from './contact-manager';
13
14
  import { HaloParty } from './halo-party';
14
15
  import { Preferences } from './preferences';
@@ -20,10 +21,10 @@ const log = debug('dxos:echo-db:identity');
20
21
  *
21
22
  * Acts as a read-only view into IdentityManager.
22
23
  */
23
- export class Identity {
24
- private _identityKey?: KeyRecord;
25
- private _deviceKey?: KeyRecord;
26
- private _deviceKeyChain?: KeyChain;
24
+ export class Identity implements IdentityCredentials {
25
+ private readonly _identityKey: KeyRecord;
26
+ private readonly _deviceKey: KeyRecord;
27
+ private readonly _deviceKeyChain: KeyChain;
27
28
 
28
29
  /**
29
30
  * @param _halo HALO party. Must be open.
@@ -31,54 +32,55 @@ export class Identity {
31
32
  constructor (
32
33
  private readonly _keyring: Keyring,
33
34
  private readonly _halo: HaloParty
34
- ) {}
35
+ ) {
36
+ this._identityKey = this._keyring.findKey(Filter.matches({ type: KeyType.IDENTITY, own: true, trusted: true })) ?? failUndefined();
37
+ this._deviceKey = this._keyring.findKey(Keyring.signingFilter({ type: KeyType.DEVICE })) ?? failUndefined();
38
+ this._deviceKeyChain = getDeviceKeyChainFromHalo(this._halo, this.deviceKey);
39
+ assert(this._halo.identityGenesis);
40
+ }
35
41
 
36
42
  get signer (): Signer {
37
43
  return this._keyring;
38
44
  }
39
45
 
40
- get identityKey (): KeyRecord | undefined {
41
- if (!this._identityKey) {
42
- this._identityKey = this._keyring.findKey(Filter.matches({ type: KeyType.IDENTITY, own: true, trusted: true }));
43
- }
46
+ get keyring (): Keyring {
47
+ return this._keyring;
48
+ }
44
49
 
50
+ get identityKey (): KeyRecord {
45
51
  return this._identityKey;
46
52
  }
47
53
 
48
- get deviceKey (): KeyRecord | undefined {
49
- if (!this._deviceKey) {
50
- this._deviceKey = this._keyring.findKey(Keyring.signingFilter({ type: KeyType.DEVICE }));
51
- }
52
-
54
+ get deviceKey (): KeyRecord {
53
55
  return this._deviceKey;
54
56
  }
55
57
 
56
- get deviceKeyChain (): KeyChain | undefined {
57
- if (!this._deviceKeyChain) {
58
- this._deviceKeyChain = this.deviceKey && this._halo ? getDeviceKeyChainFromHalo(this._halo, this.deviceKey) : undefined;
59
- }
60
-
58
+ get deviceKeyChain (): KeyChain {
61
59
  return this._deviceKeyChain;
62
60
  }
63
61
 
64
- get preferences (): Preferences | undefined {
65
- return this._halo?.preferences;
62
+ get displayName (): string | undefined {
63
+ return this.identityInfo?.signed.payload.displayName;
66
64
  }
67
65
 
68
- get contacts (): ContactManager | undefined {
69
- return this._halo?.contacts;
66
+ /**
67
+ * Contains profile username.
68
+ * Can be missing if the username wasn't provided when profile was created.
69
+ */
70
+ get identityInfo (): SignedMessage | undefined {
71
+ return this._halo.identityInfo;
70
72
  }
71
73
 
72
- get displayName (): string | undefined {
73
- return this.identityInfo?.signed.payload.displayName;
74
+ get identityGenesis (): SignedMessage {
75
+ return this._halo.identityGenesis ?? failUndefined();
74
76
  }
75
77
 
76
- get identityInfo () {
77
- return this._halo?.identityInfo;
78
+ get preferences (): Preferences {
79
+ return this._halo.preferences;
78
80
  }
79
81
 
80
- get identityGenesis () {
81
- return this._halo?.identityGenesis;
82
+ get contacts (): ContactManager {
83
+ return this._halo.contacts;
82
84
  }
83
85
 
84
86
  /**
@@ -88,16 +90,12 @@ export class Identity {
88
90
  return this._halo;
89
91
  }
90
92
 
91
- get keyring () {
92
- return this._keyring;
93
- }
94
-
95
93
  createCredentialsSigner (): CredentialsSigner {
96
94
  return new CredentialsSigner(
97
95
  this._keyring,
98
- () => this.identityKey ?? raise(new IdentityNotInitializedError()),
99
- () => this.deviceKey ?? raise(new IdentityNotInitializedError()),
100
- () => this.deviceKeyChain ?? this.deviceKey ?? raise(new IdentityNotInitializedError())
96
+ this.identityKey,
97
+ this.deviceKey,
98
+ this.deviceKeyChain
101
99
  );
102
100
  }
103
101
  }
@@ -112,7 +110,7 @@ function getDeviceKeyChainFromHalo (halo: HaloParty, deviceKey: KeyRecord) {
112
110
  halo.feedKeys
113
111
  );
114
112
  } catch (err: any) {
115
- log('Unable to locate device KeyChain:', err); // TODO(burdon): ???
116
- return undefined;
113
+ log('Unable to locate device KeyChain:', err);
114
+ throw err;
117
115
  }
118
116
  }
@@ -21,11 +21,10 @@ import {
21
21
  codec
22
22
  } from '@dxos/credentials';
23
23
  import { keyToBuffer, keyToString, PublicKey, randomBytes } from '@dxos/crypto';
24
- import { raise } from '@dxos/debug';
25
24
  import { FullyConnectedTopology, NetworkManager } from '@dxos/network-manager';
26
25
 
27
- import { IdentityNotInitializedError, InvalidInvitationError } from '../errors';
28
- import { Identity } from '../halo';
26
+ import { InvalidInvitationError } from '../errors';
27
+ import { CredentialsSigner } from '../protocol';
29
28
  import { greetingProtocolProvider } from './greeting-protocol-provider';
30
29
  import { GreetingState } from './greeting-responder';
31
30
  import { InvitationDescriptor, InvitationDescriptorType } from './invitation-descriptor';
@@ -170,17 +169,17 @@ export class OfflineInvitationClaimer {
170
169
  }
171
170
 
172
171
  // The secretProvider should provide an `Auth` message signed directly by the Identity key.
173
- static createSecretProvider (identity: Identity): SecretProvider {
172
+ static createSecretProvider (credentials: CredentialsSigner): SecretProvider {
174
173
  return async (info?: SecretInfo) => {
175
174
  return Buffer.from(codec.encode(
176
175
  /* The signed portion of the Auth message includes the ID and authNonce provided
177
176
  * by the `info` object. These values will be validated on the other end.
178
177
  */
179
178
  createAuthMessage(
180
- identity.signer,
179
+ credentials.signer,
181
180
  info!.id.value,
182
- identity.identityKey ?? raise(new IdentityNotInitializedError()),
183
- identity.deviceKeyChain ?? raise(new IdentityNotInitializedError()),
181
+ credentials.getIdentityKey(),
182
+ credentials.getDeviceSigningKeys(),
184
183
  undefined,
185
184
  info!.authNonce.value)
186
185
  ));
@@ -0,0 +1,212 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ import expect from 'expect';
6
+ import { it as test } from 'mocha';
7
+
8
+ import { createKeyAdmitMessage, createPartyGenesisMessage, defaultSecretProvider, KeyHint, Keyring, KeyType, codec as haloCodec } from '@dxos/credentials';
9
+ import { PublicKey } from '@dxos/crypto';
10
+ import { codec } from '@dxos/echo-protocol';
11
+ import { FeedStore } from '@dxos/feed-store';
12
+ import { ModelFactory } from '@dxos/model-factory';
13
+ import { NetworkManager } from '@dxos/network-manager';
14
+ import { ObjectModel } from '@dxos/object-model';
15
+ import { createStorage, StorageType } from '@dxos/random-access-multi-storage';
16
+
17
+ import { createDataPartyAdmissionMessages, defaultInvitationAuthenticator, GreetingInitiator } from '../invitations';
18
+ import { MetadataStore, PartyFeedProvider } from '../pipeline';
19
+ import { createAuthenticator, createCredentialsProvider } from '../protocol';
20
+ import { createTestIdentityCredentials, deriveTestDeviceCredentials, IdentityCredentials } from '../protocol/identity-credentials';
21
+ import { SnapshotStore } from '../snapshots';
22
+ import { createRamStorage } from '../util';
23
+ import { DataParty } from './data-party';
24
+
25
+ describe('DataParty', () => {
26
+ const createParty = async (identity: IdentityCredentials, partyKey: PublicKey, hints: KeyHint[]) => {
27
+ const metadataStore = new MetadataStore(createRamStorage());
28
+ const feedStore = new FeedStore(createStorage('', StorageType.RAM), { valueEncoding: codec });
29
+ const snapshotStore = new SnapshotStore(createStorage('', StorageType.RAM));
30
+ const modelFactory = new ModelFactory().registerModel(ObjectModel);
31
+ const networkManager = new NetworkManager();
32
+ const partyFeedProvider = new PartyFeedProvider(metadataStore, identity.keyring, feedStore, partyKey);
33
+
34
+ return new DataParty(
35
+ partyKey,
36
+ modelFactory,
37
+ snapshotStore,
38
+ partyFeedProvider,
39
+ identity.createCredentialsSigner(),
40
+ identity.preferences,
41
+ networkManager,
42
+ hints
43
+ );
44
+ };
45
+
46
+ test('open & close', async () => {
47
+ const keyring = new Keyring();
48
+ const identity = await createTestIdentityCredentials(keyring);
49
+ const partyKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
50
+ const party = await createParty(identity, partyKey.publicKey, []);
51
+
52
+ await party.open();
53
+ await party.close();
54
+ });
55
+
56
+ test('edit data', async () => {
57
+ const keyring = new Keyring();
58
+ const identity = await createTestIdentityCredentials(keyring);
59
+ const partyKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
60
+ const party = await createParty(identity, partyKey.publicKey, []);
61
+ await party.open();
62
+
63
+ const feed = await party.feedProvider.createOrOpenWritableFeed();
64
+ await party.processor.writeHaloMessage(createPartyGenesisMessage(
65
+ keyring,
66
+ partyKey,
67
+ feed.key,
68
+ partyKey
69
+ ));
70
+
71
+ await party.database.createItem({ type: 'test:item' });
72
+
73
+ await party.close();
74
+ });
75
+
76
+ test('authenticates its own credentials', async () => {
77
+ const keyring = new Keyring();
78
+ const identity = await createTestIdentityCredentials(keyring);
79
+ const partyKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
80
+
81
+ const party = await createParty(identity, partyKey.publicKey, []);
82
+ await party.open();
83
+ const feed = await party.feedProvider.createOrOpenWritableFeed();
84
+ await party.processor.writeHaloMessage(createPartyGenesisMessage(
85
+ keyring,
86
+ partyKey,
87
+ feed.key,
88
+ partyKey
89
+ ));
90
+
91
+ const authenticator = createAuthenticator(party.processor, identity.createCredentialsSigner());
92
+ const credentialsProvider = createCredentialsProvider(identity.createCredentialsSigner(), party.key, feed.key);
93
+
94
+ const wrappedCredentials = haloCodec.decode(credentialsProvider.get());
95
+ expect(await authenticator.authenticate(wrappedCredentials.payload)).toEqual(true);
96
+
97
+ await party.close();
98
+ });
99
+
100
+ test('authenticates another device', async () => {
101
+ const keyring = new Keyring();
102
+ const identityA = await createTestIdentityCredentials(keyring);
103
+ const partyKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
104
+
105
+ const party = await createParty(identityA, partyKey.publicKey, []);
106
+ await party.open();
107
+ const feed = await party.feedProvider.createOrOpenWritableFeed();
108
+ await party.processor.writeHaloMessage(createPartyGenesisMessage(
109
+ keyring,
110
+ partyKey,
111
+ feed.key,
112
+ partyKey
113
+ ));
114
+ const authenticator = createAuthenticator(party.processor, identityA.createCredentialsSigner());
115
+
116
+ const identityB = await deriveTestDeviceCredentials(identityA);
117
+ const credentialsProvider = createCredentialsProvider(identityB.createCredentialsSigner(), party.key, feed.key);
118
+
119
+ const wrappedCredentials = haloCodec.decode(credentialsProvider.get());
120
+ expect(await authenticator.authenticate(wrappedCredentials.payload)).toEqual(true);
121
+
122
+ await party.close();
123
+ });
124
+
125
+ test('two instances replicating', async () => {
126
+ const keyring = new Keyring();
127
+ const identityA = await createTestIdentityCredentials(keyring);
128
+ const partyKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
129
+
130
+ const partyA = await createParty(identityA, partyKey.publicKey, []);
131
+ await partyA.open();
132
+ const feedA = await partyA.feedProvider.createOrOpenWritableFeed();
133
+ await partyA.processor.writeHaloMessage(createPartyGenesisMessage(
134
+ keyring,
135
+ partyKey,
136
+ feedA.key,
137
+ partyKey
138
+ ));
139
+ await partyA.processor.writeHaloMessage(createKeyAdmitMessage(
140
+ keyring,
141
+ partyKey.publicKey,
142
+ identityA.identityKey,
143
+ [partyKey]
144
+ ));
145
+
146
+ const identityB = await deriveTestDeviceCredentials(identityA);
147
+ const partyB = await createParty(identityB, partyKey.publicKey, [
148
+ { type: KeyType.FEED, publicKey: feedA.key }
149
+ ]);
150
+ await partyB.open();
151
+
152
+ await partyA.database.createItem({ type: 'test:item-a' });
153
+ await partyB.database.waitForItem({ type: 'test:item-a' });
154
+
155
+ await partyB.database.createItem({ type: 'test:item-b' });
156
+ await partyA.database.waitForItem({ type: 'test:item-b' });
157
+
158
+ await partyA.close();
159
+ await partyB.close();
160
+ });
161
+
162
+ test('invitations', async () => {
163
+ const identityA = await createTestIdentityCredentials(new Keyring());
164
+ const partyKeyA = await identityA.keyring.createKeyRecord({ type: KeyType.PARTY });
165
+
166
+ const partyA = await createParty(identityA, partyKeyA.publicKey, []);
167
+ await partyA.open();
168
+ const feedA = await partyA.feedProvider.createOrOpenWritableFeed();
169
+ await partyA.processor.writeHaloMessage(createPartyGenesisMessage(
170
+ identityA.keyring,
171
+ partyKeyA,
172
+ feedA.key,
173
+ partyKeyA
174
+ ));
175
+ await partyA.processor.writeHaloMessage(createKeyAdmitMessage(
176
+ identityA.keyring,
177
+ partyKeyA.publicKey,
178
+ identityA.identityKey,
179
+ [partyKeyA]
180
+ ));
181
+
182
+ const invitation = await partyA.invitationManager.createInvitation(defaultInvitationAuthenticator);
183
+
184
+ const identityB = await createTestIdentityCredentials(new Keyring());
185
+ const initiator = new GreetingInitiator(
186
+ new NetworkManager(),
187
+ invitation,
188
+ async (partyKey, nonce) => [createDataPartyAdmissionMessages(
189
+ identityB.createCredentialsSigner(),
190
+ partyKey,
191
+ identityB.identityGenesis,
192
+ nonce
193
+ )]
194
+ );
195
+
196
+ await initiator.connect();
197
+ const { partyKey: partyKeyB, hints: hintsB } = await initiator.redeemInvitation(defaultSecretProvider);
198
+ expect(partyKeyB.equals(partyKeyA.publicKey));
199
+ const partyB = await createParty(identityB, partyKeyB, hintsB);
200
+ await partyB.open();
201
+ await initiator.destroy();
202
+
203
+ await partyA.database.createItem({ type: 'test:item-a' });
204
+ await partyB.database.waitForItem({ type: 'test:item-a' });
205
+
206
+ await partyB.database.createItem({ type: 'test:item-b' });
207
+ await partyA.database.waitForItem({ type: 'test:item-b' });
208
+
209
+ await partyA.close();
210
+ await partyB.close();
211
+ });
212
+ });
@@ -175,8 +175,7 @@ export class DataParty {
175
175
  this._networkManager,
176
176
  this._feedProvider,
177
177
  deviceKey.publicKey,
178
- createCredentialsProvider(this._credentialsSigner, this._partyCore.key, writeFeed.key),
179
- this._partyCore.processor.getActiveFeedSet()
178
+ createCredentialsProvider(this._credentialsSigner, this._partyCore.key, writeFeed.key)
180
179
  );
181
180
 
182
181
  await this._protocol.start([
@@ -22,12 +22,12 @@ import { NetworkManager } from '@dxos/network-manager';
22
22
  import { ObjectModel } from '@dxos/object-model';
23
23
 
24
24
  import { IdentityNotInitializedError } from '../errors';
25
- import { IdentityProvider } from '../halo';
26
25
  import {
27
26
  createDataPartyAdmissionMessages,
28
27
  GreetingInitiator, InvitationDescriptor, InvitationDescriptorType, OfflineInvitationClaimer
29
28
  } from '../invitations';
30
29
  import { PartyFeedProvider, PartyOptions } from '../pipeline';
30
+ import { IdentityCredentialsProvider } from '../protocol/identity-credentials';
31
31
  import { SnapshotStore } from '../snapshots';
32
32
  import { DataParty, PARTY_ITEM_TYPE } from './data-party';
33
33
 
@@ -38,7 +38,7 @@ const log = debug('dxos:echo-db:party-factory');
38
38
  */
39
39
  export class PartyFactory {
40
40
  constructor (
41
- private readonly _identityProvider: IdentityProvider,
41
+ private readonly _identityProvider: IdentityCredentialsProvider,
42
42
  private readonly _networkManager: NetworkManager,
43
43
  private readonly _modelFactory: ModelFactory,
44
44
  private readonly _snapshotStore: SnapshotStore,
@@ -52,8 +52,6 @@ export class PartyFactory {
52
52
  @timed(5_000)
53
53
  async createParty (): Promise<DataParty> {
54
54
  const identity = this._identityProvider() ?? raise(new IdentityNotInitializedError());
55
- assert(identity.identityGenesis, 'HALO not initialized.');
56
- assert(identity.deviceKeyChain, 'Device KeyChain not initialized.');
57
55
  assert(!this._options.readOnly, 'PartyFactory is read-only.');
58
56
 
59
57
  const partyKey = await identity.keyring.createKeyRecord({ type: KeyType.PARTY });
@@ -66,7 +64,7 @@ export class PartyFactory {
66
64
 
67
65
  // PartyGenesis (self-signed by Party).
68
66
  await party.processor.writeHaloMessage(createPartyGenesisMessage(
69
- identity.signer,
67
+ identity.keyring,
70
68
  partyKey,
71
69
  writableFeed.key,
72
70
  partyKey)
@@ -74,15 +72,16 @@ export class PartyFactory {
74
72
 
75
73
  // KeyAdmit (IdentityGenesis in an Envelope signed by Party).
76
74
  await party.processor.writeHaloMessage(createEnvelopeMessage(
77
- identity.signer,
75
+ identity.keyring,
78
76
  partyKey.publicKey,
79
77
  wrapMessage(identity.identityGenesis),
80
78
  [partyKey])
81
79
  );
82
80
 
83
81
  // FeedAdmit (signed by the Device KeyChain).
82
+ // TODO(dmaretskyi): Is this really needed since a feed is already admitted by party genesis message.
84
83
  await party.processor.writeHaloMessage(createFeedAdmitMessage(
85
- identity.signer,
84
+ identity.keyring,
86
85
  partyKey.publicKey,
87
86
  writableFeed.key,
88
87
  [identity.deviceKeyChain]
@@ -91,7 +90,7 @@ export class PartyFactory {
91
90
  // IdentityInfo in an Envelope signed by the Device KeyChain.
92
91
  if (identity.identityInfo) {
93
92
  await party.processor.writeHaloMessage(createEnvelopeMessage(
94
- identity.signer,
93
+ identity.keyring,
95
94
  partyKey.publicKey,
96
95
  wrapMessage(identity.identityInfo),
97
96
  [identity.deviceKeyChain]
@@ -138,13 +137,12 @@ export class PartyFactory {
138
137
  );
139
138
 
140
139
  await party.open();
141
- assert(identity.identityKey, 'No identity key.');
142
140
  const isHalo = identity.identityKey.publicKey.equals(partyKey);
143
141
  const signingKey = isHalo ? identity.deviceKey : identity.deviceKeyChain;
144
142
  assert(signingKey, 'No device key or keychain.');
145
143
  // Write the Feed genesis message.
146
144
  await party.processor.writeHaloMessage(createFeedAdmitMessage(
147
- identity.signer,
145
+ identity.keyring,
148
146
  partyKey,
149
147
  feedKeyPair.publicKey,
150
148
  [signingKey]
@@ -213,7 +211,7 @@ export class PartyFactory {
213
211
  async (partyKey, nonce) => [createDataPartyAdmissionMessages(
214
212
  identity.createCredentialsSigner(),
215
213
  partyKey,
216
- identity.identityGenesis ?? raise(new IdentityNotInitializedError()),
214
+ identity.identityGenesis,
217
215
  nonce
218
216
  )]
219
217
  );
@@ -223,13 +221,11 @@ export class PartyFactory {
223
221
  const party = await this.addParty(partyKey, hints);
224
222
  await initiator.destroy();
225
223
  if (!haloInvitation) {
226
- assert(identity.deviceKeyChain);
227
-
228
224
  // Copy our signed IdentityInfo into the new Party.
229
225
  const infoMessage = identity.identityInfo;
230
226
  if (infoMessage) {
231
227
  await party.processor.writeHaloMessage(createEnvelopeMessage(
232
- identity.signer,
228
+ identity.keyring,
233
229
  partyKey,
234
230
  wrapMessage(infoMessage),
235
231
  [identity.deviceKeyChain]
@@ -244,8 +240,6 @@ export class PartyFactory {
244
240
  const identity = this._identityProvider() ?? raise(new IdentityNotInitializedError());
245
241
 
246
242
  assert(!this._options.readOnly, 'PartyFactory is read-only');
247
- assert(identity.identityGenesis, 'IdentityGenesis must exist');
248
- assert(identity.deviceKeyChain, 'Device KeyChain must exist');
249
243
 
250
244
  const partyKey = await identity.keyring.createKeyRecord({ type: KeyType.PARTY });
251
245
  const party = await this.constructParty(partyKey.publicKey);
@@ -257,7 +251,7 @@ export class PartyFactory {
257
251
 
258
252
  // PartyGenesis (self-signed by Party).
259
253
  await party.processor.writeHaloMessage(createPartyGenesisMessage(
260
- identity.signer,
254
+ identity.keyring,
261
255
  partyKey,
262
256
  writableFeed.key,
263
257
  partyKey)
@@ -265,7 +259,7 @@ export class PartyFactory {
265
259
 
266
260
  // KeyAdmit (IdentityGenesis in an Envelope signed by Party).
267
261
  await party.processor.writeHaloMessage(createEnvelopeMessage(
268
- identity.signer,
262
+ identity.keyring,
269
263
  partyKey.publicKey,
270
264
  wrapMessage(identity.identityGenesis),
271
265
  [partyKey]
@@ -273,7 +267,7 @@ export class PartyFactory {
273
267
 
274
268
  // FeedAdmit (signed by the Device KeyChain).
275
269
  await party.processor.writeHaloMessage(createFeedAdmitMessage(
276
- identity.signer,
270
+ identity.keyring,
277
271
  partyKey.publicKey,
278
272
  writableFeed.key,
279
273
  [identity.deviceKeyChain]
@@ -282,7 +276,7 @@ export class PartyFactory {
282
276
  // IdentityInfo in an Envelope signed by the Device KeyChain.
283
277
  if (identity.identityInfo) {
284
278
  await party.processor.writeHaloMessage(createEnvelopeMessage(
285
- identity.signer,
279
+ identity.keyring,
286
280
  partyKey.publicKey,
287
281
  wrapMessage(identity.identityInfo),
288
282
  [identity.deviceKeyChain]