@dxos/echo-db 2.33.3-dev.5c4af82a → 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.
- package/dist/src/echo.js +2 -2
- package/dist/src/echo.js.map +1 -1
- package/dist/src/halo/halo-party.d.ts.map +1 -1
- package/dist/src/halo/halo-party.js +1 -1
- package/dist/src/halo/halo-party.js.map +1 -1
- package/dist/src/halo/identity.d.ts +5 -4
- package/dist/src/halo/identity.d.ts.map +1 -1
- package/dist/src/halo/identity.js +9 -9
- package/dist/src/halo/identity.js.map +1 -1
- package/dist/src/invitations/offline-invitation-claimer.d.ts +2 -2
- package/dist/src/invitations/offline-invitation-claimer.d.ts.map +1 -1
- package/dist/src/invitations/offline-invitation-claimer.js +2 -2
- package/dist/src/invitations/offline-invitation-claimer.js.map +1 -1
- package/dist/src/parties/data-party.d.ts.map +1 -1
- package/dist/src/parties/data-party.js +1 -1
- package/dist/src/parties/data-party.js.map +1 -1
- package/dist/src/parties/data-party.test.d.ts +2 -0
- package/dist/src/parties/data-party.test.d.ts.map +1 -0
- package/dist/src/parties/data-party.test.js +129 -0
- package/dist/src/parties/data-party.test.js.map +1 -0
- package/dist/src/parties/party-factory.d.ts +2 -2
- package/dist/src/parties/party-factory.d.ts.map +1 -1
- package/dist/src/parties/party-factory.js +11 -10
- package/dist/src/parties/party-factory.js.map +1 -1
- package/dist/src/parties/party-manager.d.ts +2 -2
- package/dist/src/parties/party-manager.d.ts.map +1 -1
- package/dist/src/parties/party-manager.js.map +1 -1
- package/dist/src/parties/party-manager.test.js +7 -22
- package/dist/src/parties/party-manager.test.js.map +1 -1
- package/dist/src/pipeline/metadata-store.d.ts.map +1 -1
- package/dist/src/pipeline/metadata-store.js +5 -6
- package/dist/src/pipeline/metadata-store.js.map +1 -1
- package/dist/src/pipeline/party-core.test.js +1 -1
- package/dist/src/pipeline/party-core.test.js.map +1 -1
- package/dist/src/pipeline/party-processor.d.ts +0 -5
- package/dist/src/pipeline/party-processor.d.ts.map +1 -1
- package/dist/src/pipeline/party-processor.js +0 -7
- package/dist/src/pipeline/party-processor.js.map +1 -1
- package/dist/src/protocol/identity-credentials.d.ts +22 -0
- package/dist/src/protocol/identity-credentials.d.ts.map +1 -0
- package/dist/src/protocol/identity-credentials.js +50 -0
- package/dist/src/protocol/identity-credentials.js.map +1 -0
- package/dist/src/protocol/party-protocol-factory.d.ts +3 -4
- package/dist/src/protocol/party-protocol-factory.d.ts.map +1 -1
- package/dist/src/protocol/party-protocol-factory.js +12 -15
- package/dist/src/protocol/party-protocol-factory.js.map +1 -1
- package/dist/src/snapshots/snapshot-store.d.ts.map +1 -1
- package/dist/src/snapshots/snapshot-store.js +5 -6
- package/dist/src/snapshots/snapshot-store.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +17 -17
- package/src/echo.ts +1 -1
- package/src/halo/halo-party.ts +1 -2
- package/src/halo/identity.ts +14 -13
- package/src/invitations/offline-invitation-claimer.ts +5 -5
- package/src/parties/data-party.test.ts +212 -0
- package/src/parties/data-party.ts +1 -2
- package/src/parties/party-factory.ts +13 -12
- package/src/parties/party-manager.test.ts +8 -42
- package/src/parties/party-manager.ts +2 -2
- package/src/pipeline/metadata-store.ts +5 -6
- package/src/pipeline/party-core.test.ts +2 -4
- package/src/pipeline/party-processor.ts +0 -13
- package/src/protocol/identity-credentials.ts +78 -0
- package/src/protocol/party-protocol-factory.ts +13 -17
- package/src/snapshots/snapshot-store.ts +5 -6
package/src/halo/identity.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { Filter, KeyChain, KeyRecord, Keyring, KeyType, SignedMessage, Signer }
|
|
|
9
9
|
import { failUndefined } from '@dxos/debug';
|
|
10
10
|
|
|
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,7 +21,7 @@ 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
|
+
export class Identity implements IdentityCredentials {
|
|
24
25
|
private readonly _identityKey: KeyRecord;
|
|
25
26
|
private readonly _deviceKey: KeyRecord;
|
|
26
27
|
private readonly _deviceKeyChain: KeyChain;
|
|
@@ -42,6 +43,10 @@ export class Identity {
|
|
|
42
43
|
return this._keyring;
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
get keyring (): Keyring {
|
|
47
|
+
return this._keyring;
|
|
48
|
+
}
|
|
49
|
+
|
|
45
50
|
get identityKey (): KeyRecord {
|
|
46
51
|
return this._identityKey;
|
|
47
52
|
}
|
|
@@ -54,14 +59,6 @@ export class Identity {
|
|
|
54
59
|
return this._deviceKeyChain;
|
|
55
60
|
}
|
|
56
61
|
|
|
57
|
-
get preferences (): Preferences {
|
|
58
|
-
return this._halo.preferences;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
get contacts (): ContactManager {
|
|
62
|
-
return this._halo.contacts;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
62
|
get displayName (): string | undefined {
|
|
66
63
|
return this.identityInfo?.signed.payload.displayName;
|
|
67
64
|
}
|
|
@@ -78,6 +75,14 @@ export class Identity {
|
|
|
78
75
|
return this._halo.identityGenesis ?? failUndefined();
|
|
79
76
|
}
|
|
80
77
|
|
|
78
|
+
get preferences (): Preferences {
|
|
79
|
+
return this._halo.preferences;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get contacts (): ContactManager {
|
|
83
|
+
return this._halo.contacts;
|
|
84
|
+
}
|
|
85
|
+
|
|
81
86
|
/**
|
|
82
87
|
* HALO party. Must be open.
|
|
83
88
|
*/
|
|
@@ -85,10 +90,6 @@ export class Identity {
|
|
|
85
90
|
return this._halo;
|
|
86
91
|
}
|
|
87
92
|
|
|
88
|
-
get keyring () {
|
|
89
|
-
return this._keyring;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
93
|
createCredentialsSigner (): CredentialsSigner {
|
|
93
94
|
return new CredentialsSigner(
|
|
94
95
|
this._keyring,
|
|
@@ -24,7 +24,7 @@ import { keyToBuffer, keyToString, PublicKey, randomBytes } from '@dxos/crypto';
|
|
|
24
24
|
import { FullyConnectedTopology, NetworkManager } from '@dxos/network-manager';
|
|
25
25
|
|
|
26
26
|
import { InvalidInvitationError } from '../errors';
|
|
27
|
-
import {
|
|
27
|
+
import { CredentialsSigner } from '../protocol';
|
|
28
28
|
import { greetingProtocolProvider } from './greeting-protocol-provider';
|
|
29
29
|
import { GreetingState } from './greeting-responder';
|
|
30
30
|
import { InvitationDescriptor, InvitationDescriptorType } from './invitation-descriptor';
|
|
@@ -169,17 +169,17 @@ export class OfflineInvitationClaimer {
|
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
// The secretProvider should provide an `Auth` message signed directly by the Identity key.
|
|
172
|
-
static createSecretProvider (
|
|
172
|
+
static createSecretProvider (credentials: CredentialsSigner): SecretProvider {
|
|
173
173
|
return async (info?: SecretInfo) => {
|
|
174
174
|
return Buffer.from(codec.encode(
|
|
175
175
|
/* The signed portion of the Auth message includes the ID and authNonce provided
|
|
176
176
|
* by the `info` object. These values will be validated on the other end.
|
|
177
177
|
*/
|
|
178
178
|
createAuthMessage(
|
|
179
|
-
|
|
179
|
+
credentials.signer,
|
|
180
180
|
info!.id.value,
|
|
181
|
-
|
|
182
|
-
|
|
181
|
+
credentials.getIdentityKey(),
|
|
182
|
+
credentials.getDeviceSigningKeys(),
|
|
183
183
|
undefined,
|
|
184
184
|
info!.authNonce.value)
|
|
185
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:
|
|
41
|
+
private readonly _identityProvider: IdentityCredentialsProvider,
|
|
42
42
|
private readonly _networkManager: NetworkManager,
|
|
43
43
|
private readonly _modelFactory: ModelFactory,
|
|
44
44
|
private readonly _snapshotStore: SnapshotStore,
|
|
@@ -64,7 +64,7 @@ export class PartyFactory {
|
|
|
64
64
|
|
|
65
65
|
// PartyGenesis (self-signed by Party).
|
|
66
66
|
await party.processor.writeHaloMessage(createPartyGenesisMessage(
|
|
67
|
-
identity.
|
|
67
|
+
identity.keyring,
|
|
68
68
|
partyKey,
|
|
69
69
|
writableFeed.key,
|
|
70
70
|
partyKey)
|
|
@@ -72,15 +72,16 @@ export class PartyFactory {
|
|
|
72
72
|
|
|
73
73
|
// KeyAdmit (IdentityGenesis in an Envelope signed by Party).
|
|
74
74
|
await party.processor.writeHaloMessage(createEnvelopeMessage(
|
|
75
|
-
identity.
|
|
75
|
+
identity.keyring,
|
|
76
76
|
partyKey.publicKey,
|
|
77
77
|
wrapMessage(identity.identityGenesis),
|
|
78
78
|
[partyKey])
|
|
79
79
|
);
|
|
80
80
|
|
|
81
81
|
// FeedAdmit (signed by the Device KeyChain).
|
|
82
|
+
// TODO(dmaretskyi): Is this really needed since a feed is already admitted by party genesis message.
|
|
82
83
|
await party.processor.writeHaloMessage(createFeedAdmitMessage(
|
|
83
|
-
identity.
|
|
84
|
+
identity.keyring,
|
|
84
85
|
partyKey.publicKey,
|
|
85
86
|
writableFeed.key,
|
|
86
87
|
[identity.deviceKeyChain]
|
|
@@ -89,7 +90,7 @@ export class PartyFactory {
|
|
|
89
90
|
// IdentityInfo in an Envelope signed by the Device KeyChain.
|
|
90
91
|
if (identity.identityInfo) {
|
|
91
92
|
await party.processor.writeHaloMessage(createEnvelopeMessage(
|
|
92
|
-
identity.
|
|
93
|
+
identity.keyring,
|
|
93
94
|
partyKey.publicKey,
|
|
94
95
|
wrapMessage(identity.identityInfo),
|
|
95
96
|
[identity.deviceKeyChain]
|
|
@@ -141,7 +142,7 @@ export class PartyFactory {
|
|
|
141
142
|
assert(signingKey, 'No device key or keychain.');
|
|
142
143
|
// Write the Feed genesis message.
|
|
143
144
|
await party.processor.writeHaloMessage(createFeedAdmitMessage(
|
|
144
|
-
identity.
|
|
145
|
+
identity.keyring,
|
|
145
146
|
partyKey,
|
|
146
147
|
feedKeyPair.publicKey,
|
|
147
148
|
[signingKey]
|
|
@@ -224,7 +225,7 @@ export class PartyFactory {
|
|
|
224
225
|
const infoMessage = identity.identityInfo;
|
|
225
226
|
if (infoMessage) {
|
|
226
227
|
await party.processor.writeHaloMessage(createEnvelopeMessage(
|
|
227
|
-
identity.
|
|
228
|
+
identity.keyring,
|
|
228
229
|
partyKey,
|
|
229
230
|
wrapMessage(infoMessage),
|
|
230
231
|
[identity.deviceKeyChain]
|
|
@@ -250,7 +251,7 @@ export class PartyFactory {
|
|
|
250
251
|
|
|
251
252
|
// PartyGenesis (self-signed by Party).
|
|
252
253
|
await party.processor.writeHaloMessage(createPartyGenesisMessage(
|
|
253
|
-
identity.
|
|
254
|
+
identity.keyring,
|
|
254
255
|
partyKey,
|
|
255
256
|
writableFeed.key,
|
|
256
257
|
partyKey)
|
|
@@ -258,7 +259,7 @@ export class PartyFactory {
|
|
|
258
259
|
|
|
259
260
|
// KeyAdmit (IdentityGenesis in an Envelope signed by Party).
|
|
260
261
|
await party.processor.writeHaloMessage(createEnvelopeMessage(
|
|
261
|
-
identity.
|
|
262
|
+
identity.keyring,
|
|
262
263
|
partyKey.publicKey,
|
|
263
264
|
wrapMessage(identity.identityGenesis),
|
|
264
265
|
[partyKey]
|
|
@@ -266,7 +267,7 @@ export class PartyFactory {
|
|
|
266
267
|
|
|
267
268
|
// FeedAdmit (signed by the Device KeyChain).
|
|
268
269
|
await party.processor.writeHaloMessage(createFeedAdmitMessage(
|
|
269
|
-
identity.
|
|
270
|
+
identity.keyring,
|
|
270
271
|
partyKey.publicKey,
|
|
271
272
|
writableFeed.key,
|
|
272
273
|
[identity.deviceKeyChain]
|
|
@@ -275,7 +276,7 @@ export class PartyFactory {
|
|
|
275
276
|
// IdentityInfo in an Envelope signed by the Device KeyChain.
|
|
276
277
|
if (identity.identityInfo) {
|
|
277
278
|
await party.processor.writeHaloMessage(createEnvelopeMessage(
|
|
278
|
-
identity.
|
|
279
|
+
identity.keyring,
|
|
279
280
|
partyKey.publicKey,
|
|
280
281
|
wrapMessage(identity.identityInfo),
|
|
281
282
|
[identity.deviceKeyChain]
|
|
@@ -33,9 +33,9 @@ import { createStorage, StorageType } from '@dxos/random-access-multi-storage';
|
|
|
33
33
|
import { afterTest, testTimeout } from '@dxos/testutils';
|
|
34
34
|
|
|
35
35
|
import { Item } from '../api';
|
|
36
|
-
import { HaloFactory, Identity, IdentityManager } from '../halo';
|
|
37
36
|
import { defaultInvitationAuthenticator, OfflineInvitationClaimer } from '../invitations';
|
|
38
37
|
import { MetadataStore, PartyFeedProvider } from '../pipeline';
|
|
38
|
+
import { createTestIdentityCredentials } from '../protocol/identity-credentials';
|
|
39
39
|
import { SnapshotStore } from '../snapshots';
|
|
40
40
|
import { messageLogger } from '../testing';
|
|
41
41
|
import { createRamStorage } from '../util';
|
|
@@ -58,33 +58,12 @@ const setup = async () => {
|
|
|
58
58
|
const keyring = new Keyring();
|
|
59
59
|
const metadataStore = new MetadataStore(createRamStorage());
|
|
60
60
|
const feedStore = new FeedStore(createStorage('', StorageType.RAM), { valueEncoding: codec });
|
|
61
|
-
|
|
62
|
-
const identityKey = await keyring.createKeyRecord({ type: KeyType.IDENTITY });
|
|
63
|
-
|
|
64
|
-
assert(keyring.keys.length === 1);
|
|
65
|
-
|
|
66
61
|
const snapshotStore = new SnapshotStore(createStorage('', StorageType.RAM));
|
|
67
62
|
const modelFactory = new ModelFactory().registerModel(ObjectModel);
|
|
68
63
|
const networkManager = new NetworkManager();
|
|
69
64
|
const feedProviderFactory = (partyKey: PublicKey) => new PartyFeedProvider(metadataStore, keyring, feedStore, partyKey);
|
|
70
65
|
|
|
71
|
-
const
|
|
72
|
-
networkManager,
|
|
73
|
-
modelFactory,
|
|
74
|
-
snapshotStore,
|
|
75
|
-
feedProviderFactory,
|
|
76
|
-
keyring,
|
|
77
|
-
{
|
|
78
|
-
writeLogger: messageLogger('<<<'),
|
|
79
|
-
readLogger: messageLogger('>>>')
|
|
80
|
-
}
|
|
81
|
-
);
|
|
82
|
-
const haloParty = await haloFactory.createHalo({
|
|
83
|
-
identityDisplayName: identityKey.publicKey.humanize()
|
|
84
|
-
});
|
|
85
|
-
afterTest(() => haloParty.close());
|
|
86
|
-
const identity = new Identity(keyring, haloParty);
|
|
87
|
-
|
|
66
|
+
const identity = await createTestIdentityCredentials(keyring);
|
|
88
67
|
const partyFactory = new PartyFactory(
|
|
89
68
|
() => identity,
|
|
90
69
|
networkManager,
|
|
@@ -171,33 +150,22 @@ describe('Party manager', () => {
|
|
|
171
150
|
test('Create from cold start', async () => {
|
|
172
151
|
const storage = createStorage('', StorageType.RAM);
|
|
173
152
|
const feedStore = new FeedStore(storage, { valueEncoding: codec });
|
|
174
|
-
|
|
175
153
|
const keyring = new Keyring();
|
|
176
154
|
const metadataStore = new MetadataStore(createRamStorage());
|
|
177
|
-
|
|
178
|
-
const identityKey = await keyring.createKeyRecord({ type: KeyType.IDENTITY });
|
|
179
|
-
await keyring.createKeyRecord({ type: KeyType.DEVICE });
|
|
180
|
-
|
|
181
155
|
const modelFactory = new ModelFactory().registerModel(ObjectModel);
|
|
182
156
|
const snapshotStore = new SnapshotStore(createStorage('', StorageType.RAM));
|
|
183
157
|
const networkManager = new NetworkManager();
|
|
184
158
|
const feedProviderFactory = (partyKey: PublicKey) => new PartyFeedProvider(metadataStore, keyring, feedStore, partyKey);
|
|
159
|
+
|
|
160
|
+
const identity = await createTestIdentityCredentials(keyring);
|
|
185
161
|
const partyFactory = new PartyFactory(
|
|
186
|
-
() =>
|
|
162
|
+
() => identity,
|
|
187
163
|
networkManager,
|
|
188
164
|
modelFactory,
|
|
189
165
|
snapshotStore,
|
|
190
166
|
feedProviderFactory
|
|
191
167
|
);
|
|
192
|
-
const
|
|
193
|
-
networkManager,
|
|
194
|
-
modelFactory,
|
|
195
|
-
snapshotStore,
|
|
196
|
-
feedProviderFactory,
|
|
197
|
-
keyring
|
|
198
|
-
);
|
|
199
|
-
const identityManager = new IdentityManager(keyring, haloFactory, metadataStore);
|
|
200
|
-
const partyManager = new PartyManager(metadataStore, snapshotStore, () => identityManager.identity, partyFactory);
|
|
168
|
+
const partyManager = new PartyManager(metadataStore, snapshotStore, () => identity, partyFactory);
|
|
201
169
|
|
|
202
170
|
/* TODO(telackey): Injecting "raw" Parties into the feeds behind the scenes seems fishy to me, as it writes the
|
|
203
171
|
* Party messages in a slightly different way than the code inside PartyFactory does, and so could easily diverge
|
|
@@ -221,7 +189,7 @@ describe('Party manager', () => {
|
|
|
221
189
|
assert(feedKey);
|
|
222
190
|
|
|
223
191
|
const feedStream = createWritableFeedStream(feed);
|
|
224
|
-
feedStream.write({ halo: createPartyGenesisMessage(keyring, partyKey, feedKey.publicKey, identityKey) });
|
|
192
|
+
feedStream.write({ halo: createPartyGenesisMessage(keyring, partyKey, feedKey.publicKey, identity.identityKey) });
|
|
225
193
|
feedStream.write({
|
|
226
194
|
echo: checkType<EchoEnvelope>({
|
|
227
195
|
itemId: 'foo',
|
|
@@ -235,8 +203,6 @@ describe('Party manager', () => {
|
|
|
235
203
|
}
|
|
236
204
|
|
|
237
205
|
// Open.
|
|
238
|
-
await identityManager.createHalo();
|
|
239
|
-
afterTest(() => identityManager.close());
|
|
240
206
|
await partyManager.open();
|
|
241
207
|
expect(partyManager.parties).toHaveLength(numParties);
|
|
242
208
|
await partyManager.close();
|
|
@@ -450,7 +416,7 @@ describe('Party manager', () => {
|
|
|
450
416
|
// Redeem the invitation on B.
|
|
451
417
|
expect(partyManagerB.parties).toHaveLength(0);
|
|
452
418
|
const partyB = await partyManagerB.joinParty(invitationDescriptor,
|
|
453
|
-
OfflineInvitationClaimer.createSecretProvider(identityB));
|
|
419
|
+
OfflineInvitationClaimer.createSecretProvider(identityB.createCredentialsSigner()));
|
|
454
420
|
expect(partyB).toBeDefined();
|
|
455
421
|
log(`Joined ${partyB.key.toHex()}`);
|
|
456
422
|
|
|
@@ -13,9 +13,9 @@ import { timed } from '@dxos/debug';
|
|
|
13
13
|
import { PartyKey, PartySnapshot } from '@dxos/echo-protocol';
|
|
14
14
|
import { ComplexMap, boolGuard } from '@dxos/util';
|
|
15
15
|
|
|
16
|
-
import { IdentityProvider } from '../halo';
|
|
17
16
|
import { InvitationDescriptor } from '../invitations';
|
|
18
17
|
import { MetadataStore } from '../pipeline';
|
|
18
|
+
import { IdentityCredentialsProvider } from '../protocol/identity-credentials';
|
|
19
19
|
import { SnapshotStore } from '../snapshots';
|
|
20
20
|
import { DataParty, PARTY_ITEM_TYPE, PARTY_TITLE_PROPERTY } from './data-party';
|
|
21
21
|
import { PartyFactory } from './party-factory';
|
|
@@ -51,7 +51,7 @@ export class PartyManager {
|
|
|
51
51
|
constructor (
|
|
52
52
|
private readonly _metadataStore: MetadataStore,
|
|
53
53
|
private readonly _snapshotStore: SnapshotStore,
|
|
54
|
-
private readonly _identityProvider:
|
|
54
|
+
private readonly _identityProvider: IdentityCredentialsProvider,
|
|
55
55
|
private readonly _partyFactory: PartyFactory
|
|
56
56
|
) {}
|
|
57
57
|
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
import assert from 'assert';
|
|
6
6
|
import debug from 'debug';
|
|
7
|
-
import pify from 'pify';
|
|
8
7
|
|
|
9
8
|
import { PublicKey } from '@dxos/crypto';
|
|
10
9
|
import { failUndefined } from '@dxos/debug';
|
|
@@ -50,12 +49,12 @@ export class MetadataStore {
|
|
|
50
49
|
async load (): Promise<void> {
|
|
51
50
|
const file = this._storage.createOrOpen('EchoMetadata');
|
|
52
51
|
try {
|
|
53
|
-
const { size } = await
|
|
52
|
+
const { size } = await file.stat();
|
|
54
53
|
if (size === 0) {
|
|
55
54
|
return;
|
|
56
55
|
}
|
|
57
56
|
|
|
58
|
-
const data = await
|
|
57
|
+
const data = await file.read(0, size);
|
|
59
58
|
this._metadata = schema.getCodecForType('dxos.echo.metadata.EchoMetadata').decode(data);
|
|
60
59
|
} catch (err: any) {
|
|
61
60
|
if (err.code === 'ENOENT') {
|
|
@@ -64,7 +63,7 @@ export class MetadataStore {
|
|
|
64
63
|
throw err;
|
|
65
64
|
}
|
|
66
65
|
} finally {
|
|
67
|
-
await
|
|
66
|
+
await file.close();
|
|
68
67
|
}
|
|
69
68
|
}
|
|
70
69
|
|
|
@@ -80,9 +79,9 @@ export class MetadataStore {
|
|
|
80
79
|
|
|
81
80
|
try {
|
|
82
81
|
const encoded = Buffer.from(schema.getCodecForType('dxos.echo.metadata.EchoMetadata').encode(data));
|
|
83
|
-
await
|
|
82
|
+
await file.write(0, encoded);
|
|
84
83
|
} finally {
|
|
85
|
-
await
|
|
84
|
+
await file.close();
|
|
86
85
|
}
|
|
87
86
|
}
|
|
88
87
|
|
|
@@ -325,12 +325,10 @@ describe('PartyCore', () => {
|
|
|
325
325
|
|
|
326
326
|
createTestProtocolPair(
|
|
327
327
|
new ReplicatorProtocolPluginFactory(
|
|
328
|
-
peer1.partyFeedProvider
|
|
329
|
-
peer1.party.processor.getActiveFeedSet()
|
|
328
|
+
peer1.partyFeedProvider
|
|
330
329
|
).createPlugins().map(r => r.createExtension()),
|
|
331
330
|
new ReplicatorProtocolPluginFactory(
|
|
332
|
-
partyFeedProvider
|
|
333
|
-
peer1.party.processor.getActiveFeedSet()
|
|
331
|
+
partyFeedProvider
|
|
334
332
|
).createPlugins().map(r => r.createExtension())
|
|
335
333
|
);
|
|
336
334
|
|
|
@@ -20,11 +20,6 @@ import { jsonReplacer } from '@dxos/util';
|
|
|
20
20
|
|
|
21
21
|
const log = debug('dxos:echo-db:party-processor');
|
|
22
22
|
|
|
23
|
-
export interface FeedSetProvider {
|
|
24
|
-
get(): FeedKey[]
|
|
25
|
-
added: Event<FeedKey>
|
|
26
|
-
}
|
|
27
|
-
|
|
28
23
|
/**
|
|
29
24
|
* TODO(burdon): Wrapper/Bridge between HALO APIs.
|
|
30
25
|
*/
|
|
@@ -108,14 +103,6 @@ export class PartyProcessor {
|
|
|
108
103
|
return this._state.getAdmittedBy(feedKey);
|
|
109
104
|
}
|
|
110
105
|
|
|
111
|
-
// TODO(burdon): Rename xxxProvider.
|
|
112
|
-
getActiveFeedSet (): FeedSetProvider {
|
|
113
|
-
return {
|
|
114
|
-
get: () => this.feedKeys,
|
|
115
|
-
added: this.feedAdded
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
106
|
getOfflineInvitation (invitationID: Buffer) {
|
|
120
107
|
return this._state.getInvitation(invitationID);
|
|
121
108
|
}
|