@dxos/echo-db 2.33.2 → 2.33.3-dev.0cff904f
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/echo.test.js +10 -10
- package/dist/src/echo.test.js.map +1 -1
- package/dist/src/halo/halo.d.ts +1 -0
- package/dist/src/halo/halo.d.ts.map +1 -1
- package/dist/src/halo/halo.js +10 -1
- package/dist/src/halo/halo.js.map +1 -1
- package/dist/src/halo/identity-manager.d.ts +0 -1
- package/dist/src/halo/identity-manager.d.ts.map +1 -1
- package/dist/src/halo/identity-manager.js +11 -11
- package/dist/src/halo/identity-manager.js.map +1 -1
- package/dist/src/halo/identity.d.ts +18 -13
- package/dist/src/halo/identity.d.ts.map +1 -1
- package/dist/src/halo/identity.js +24 -27
- 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 -4
- package/dist/src/invitations/offline-invitation-claimer.js.map +1 -1
- 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 -20
- 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 +2 -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/protocol/credentials-signer.d.ts +4 -4
- package/dist/src/protocol/credentials-signer.d.ts.map +1 -1
- package/dist/src/protocol/credentials-signer.js +8 -8
- package/dist/src/protocol/credentials-signer.js.map +1 -1
- package/dist/src/protocol/identity-credentials.d.ts +21 -0
- package/dist/src/protocol/identity-credentials.d.ts.map +1 -0
- package/dist/src/protocol/identity-credentials.js +35 -0
- package/dist/src/protocol/identity-credentials.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +17 -17
- package/src/echo.test.ts +10 -10
- package/src/echo.ts +1 -1
- package/src/halo/halo.ts +8 -1
- package/src/halo/identity-manager.ts +13 -14
- package/src/halo/identity.ts +39 -41
- package/src/invitations/offline-invitation-claimer.ts +6 -7
- package/src/parties/party-factory.ts +13 -20
- package/src/parties/party-manager.test.ts +8 -42
- package/src/parties/party-manager.ts +4 -3
- package/src/protocol/credentials-signer.ts +9 -9
- package/src/protocol/identity-credentials.ts +56 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/echo-db",
|
|
3
|
-
"version": "2.33.
|
|
3
|
+
"version": "2.33.3-dev.0cff904f",
|
|
4
4
|
"description": "ECHO database.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -10,22 +10,22 @@
|
|
|
10
10
|
"src"
|
|
11
11
|
],
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@dxos/async": "2.33.
|
|
14
|
-
"@dxos/codec-protobuf": "2.33.
|
|
15
|
-
"@dxos/credentials": "2.33.
|
|
16
|
-
"@dxos/crypto": "2.33.
|
|
17
|
-
"@dxos/debug": "2.33.
|
|
18
|
-
"@dxos/echo-protocol": "2.33.
|
|
19
|
-
"@dxos/feed-store": "2.33.
|
|
20
|
-
"@dxos/mesh-protocol": "2.33.
|
|
21
|
-
"@dxos/model-factory": "2.33.
|
|
22
|
-
"@dxos/network-manager": "2.33.
|
|
23
|
-
"@dxos/object-model": "2.33.
|
|
24
|
-
"@dxos/protocol-plugin-presence": "2.33.
|
|
25
|
-
"@dxos/protocol-plugin-replicator": "2.33.
|
|
26
|
-
"@dxos/protocols": "2.33.
|
|
27
|
-
"@dxos/random-access-multi-storage": "2.33.
|
|
28
|
-
"@dxos/util": "2.33.
|
|
13
|
+
"@dxos/async": "2.33.3-dev.0cff904f",
|
|
14
|
+
"@dxos/codec-protobuf": "2.33.3-dev.0cff904f",
|
|
15
|
+
"@dxos/credentials": "2.33.3-dev.0cff904f",
|
|
16
|
+
"@dxos/crypto": "2.33.3-dev.0cff904f",
|
|
17
|
+
"@dxos/debug": "2.33.3-dev.0cff904f",
|
|
18
|
+
"@dxos/echo-protocol": "2.33.3-dev.0cff904f",
|
|
19
|
+
"@dxos/feed-store": "2.33.3-dev.0cff904f",
|
|
20
|
+
"@dxos/mesh-protocol": "2.33.3-dev.0cff904f",
|
|
21
|
+
"@dxos/model-factory": "2.33.3-dev.0cff904f",
|
|
22
|
+
"@dxos/network-manager": "2.33.3-dev.0cff904f",
|
|
23
|
+
"@dxos/object-model": "2.33.3-dev.0cff904f",
|
|
24
|
+
"@dxos/protocol-plugin-presence": "2.33.3-dev.0cff904f",
|
|
25
|
+
"@dxos/protocol-plugin-replicator": "2.33.3-dev.0cff904f",
|
|
26
|
+
"@dxos/protocols": "2.33.3-dev.0cff904f",
|
|
27
|
+
"@dxos/random-access-multi-storage": "2.33.3-dev.0cff904f",
|
|
28
|
+
"@dxos/util": "2.33.3-dev.0cff904f",
|
|
29
29
|
"base-x": "~3.0.9",
|
|
30
30
|
"buffer-json-encoding": "^1.0.2",
|
|
31
31
|
"debug": "^4.3.3",
|
package/src/echo.test.ts
CHANGED
|
@@ -264,8 +264,8 @@ describe('ECHO', () => {
|
|
|
264
264
|
const a = await setup({ createProfile: true });
|
|
265
265
|
const b = await setup();
|
|
266
266
|
|
|
267
|
-
expect(a.halo.
|
|
268
|
-
expect(b.halo.
|
|
267
|
+
expect(!!a.halo.identity).toEqual(true);
|
|
268
|
+
expect(!!b.halo.identity).toEqual(false);
|
|
269
269
|
|
|
270
270
|
expect(a.queryParties().value.length).toBe(0);
|
|
271
271
|
await a.createParty();
|
|
@@ -430,11 +430,11 @@ describe('ECHO', () => {
|
|
|
430
430
|
const b1 = await setup({ createProfile: true });
|
|
431
431
|
const b2 = await setup();
|
|
432
432
|
|
|
433
|
-
expect(a1.halo.
|
|
434
|
-
expect(a2.halo.
|
|
433
|
+
expect(a1.halo.identity).toBeTruthy();
|
|
434
|
+
expect(a2.halo.identity).toBeFalsy();
|
|
435
435
|
|
|
436
|
-
expect(b1.halo.
|
|
437
|
-
expect(b2.halo.
|
|
436
|
+
expect(b1.halo.identity).toBeTruthy();
|
|
437
|
+
expect(b2.halo.identity).toBeFalsy();
|
|
438
438
|
|
|
439
439
|
await Promise.all([
|
|
440
440
|
(async () => {
|
|
@@ -529,13 +529,13 @@ describe('ECHO', () => {
|
|
|
529
529
|
|
|
530
530
|
const b = await setup();
|
|
531
531
|
|
|
532
|
-
expect(a.halo.
|
|
533
|
-
expect(b.halo.
|
|
532
|
+
expect(a.halo.identity).toBeTruthy();
|
|
533
|
+
expect(b.halo.identity).toBeFalsy();
|
|
534
534
|
|
|
535
535
|
// And then redeem it on nodeB.
|
|
536
536
|
await b.halo.recover(seedPhrase);
|
|
537
|
-
expect(a.halo.
|
|
538
|
-
expect(b.halo.
|
|
537
|
+
expect(a.halo.identity).toBeTruthy();
|
|
538
|
+
expect(b.halo.identity).toBeTruthy();
|
|
539
539
|
|
|
540
540
|
// Now create a Party on A and make sure it gets opened on both A and B.
|
|
541
541
|
expect(a.queryParties().value.length).toBe(0);
|
package/src/echo.ts
CHANGED
|
@@ -376,7 +376,7 @@ export class ECHO {
|
|
|
376
376
|
assert(this._partyManager.isOpen, new InvalidStateError());
|
|
377
377
|
|
|
378
378
|
const actualSecretProvider =
|
|
379
|
-
secretProvider ?? OfflineInvitationClaimer.createSecretProvider(this.halo.identity ?? raise(new IdentityNotInitializedError()));
|
|
379
|
+
secretProvider ?? OfflineInvitationClaimer.createSecretProvider(this.halo.identity?.createCredentialsSigner() ?? raise(new IdentityNotInitializedError()));
|
|
380
380
|
|
|
381
381
|
return this._partyManager.joinParty(invitationDescriptor, actualSecretProvider);
|
|
382
382
|
}
|
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.
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
//
|
|
52
|
-
|
|
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
|
-
|
|
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 () {
|
package/src/halo/identity.ts
CHANGED
|
@@ -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 {
|
|
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
|
|
25
|
-
private _deviceKey
|
|
26
|
-
private _deviceKeyChain
|
|
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
|
|
41
|
-
|
|
42
|
-
|
|
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
|
|
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
|
|
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
|
|
65
|
-
return this.
|
|
62
|
+
get displayName (): string | undefined {
|
|
63
|
+
return this.identityInfo?.signed.payload.displayName;
|
|
66
64
|
}
|
|
67
65
|
|
|
68
|
-
|
|
69
|
-
|
|
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
|
|
73
|
-
return this.
|
|
74
|
+
get identityGenesis (): SignedMessage {
|
|
75
|
+
return this._halo.identityGenesis ?? failUndefined();
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
get
|
|
77
|
-
return this._halo
|
|
78
|
+
get preferences (): Preferences {
|
|
79
|
+
return this._halo.preferences;
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
get
|
|
81
|
-
return this._halo
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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);
|
|
116
|
-
|
|
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 {
|
|
28
|
-
import {
|
|
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 (
|
|
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
|
-
|
|
179
|
+
credentials.signer,
|
|
181
180
|
info!.id.value,
|
|
182
|
-
|
|
183
|
-
|
|
181
|
+
credentials.getIdentityKey(),
|
|
182
|
+
credentials.getDeviceSigningKeys(),
|
|
184
183
|
undefined,
|
|
185
184
|
info!.authNonce.value)
|
|
186
185
|
));
|
|
@@ -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,
|
|
@@ -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.
|
|
67
|
+
identity.keyring,
|
|
70
68
|
partyKey,
|
|
71
69
|
writableFeed.key,
|
|
72
70
|
partyKey)
|
|
@@ -74,7 +72,7 @@ export class PartyFactory {
|
|
|
74
72
|
|
|
75
73
|
// KeyAdmit (IdentityGenesis in an Envelope signed by Party).
|
|
76
74
|
await party.processor.writeHaloMessage(createEnvelopeMessage(
|
|
77
|
-
identity.
|
|
75
|
+
identity.keyring,
|
|
78
76
|
partyKey.publicKey,
|
|
79
77
|
wrapMessage(identity.identityGenesis),
|
|
80
78
|
[partyKey])
|
|
@@ -82,7 +80,7 @@ export class PartyFactory {
|
|
|
82
80
|
|
|
83
81
|
// FeedAdmit (signed by the Device KeyChain).
|
|
84
82
|
await party.processor.writeHaloMessage(createFeedAdmitMessage(
|
|
85
|
-
identity.
|
|
83
|
+
identity.keyring,
|
|
86
84
|
partyKey.publicKey,
|
|
87
85
|
writableFeed.key,
|
|
88
86
|
[identity.deviceKeyChain]
|
|
@@ -91,7 +89,7 @@ export class PartyFactory {
|
|
|
91
89
|
// IdentityInfo in an Envelope signed by the Device KeyChain.
|
|
92
90
|
if (identity.identityInfo) {
|
|
93
91
|
await party.processor.writeHaloMessage(createEnvelopeMessage(
|
|
94
|
-
identity.
|
|
92
|
+
identity.keyring,
|
|
95
93
|
partyKey.publicKey,
|
|
96
94
|
wrapMessage(identity.identityInfo),
|
|
97
95
|
[identity.deviceKeyChain]
|
|
@@ -138,13 +136,12 @@ export class PartyFactory {
|
|
|
138
136
|
);
|
|
139
137
|
|
|
140
138
|
await party.open();
|
|
141
|
-
assert(identity.identityKey, 'No identity key.');
|
|
142
139
|
const isHalo = identity.identityKey.publicKey.equals(partyKey);
|
|
143
140
|
const signingKey = isHalo ? identity.deviceKey : identity.deviceKeyChain;
|
|
144
141
|
assert(signingKey, 'No device key or keychain.');
|
|
145
142
|
// Write the Feed genesis message.
|
|
146
143
|
await party.processor.writeHaloMessage(createFeedAdmitMessage(
|
|
147
|
-
identity.
|
|
144
|
+
identity.keyring,
|
|
148
145
|
partyKey,
|
|
149
146
|
feedKeyPair.publicKey,
|
|
150
147
|
[signingKey]
|
|
@@ -213,7 +210,7 @@ export class PartyFactory {
|
|
|
213
210
|
async (partyKey, nonce) => [createDataPartyAdmissionMessages(
|
|
214
211
|
identity.createCredentialsSigner(),
|
|
215
212
|
partyKey,
|
|
216
|
-
identity.identityGenesis
|
|
213
|
+
identity.identityGenesis,
|
|
217
214
|
nonce
|
|
218
215
|
)]
|
|
219
216
|
);
|
|
@@ -223,13 +220,11 @@ export class PartyFactory {
|
|
|
223
220
|
const party = await this.addParty(partyKey, hints);
|
|
224
221
|
await initiator.destroy();
|
|
225
222
|
if (!haloInvitation) {
|
|
226
|
-
assert(identity.deviceKeyChain);
|
|
227
|
-
|
|
228
223
|
// Copy our signed IdentityInfo into the new Party.
|
|
229
224
|
const infoMessage = identity.identityInfo;
|
|
230
225
|
if (infoMessage) {
|
|
231
226
|
await party.processor.writeHaloMessage(createEnvelopeMessage(
|
|
232
|
-
identity.
|
|
227
|
+
identity.keyring,
|
|
233
228
|
partyKey,
|
|
234
229
|
wrapMessage(infoMessage),
|
|
235
230
|
[identity.deviceKeyChain]
|
|
@@ -244,8 +239,6 @@ export class PartyFactory {
|
|
|
244
239
|
const identity = this._identityProvider() ?? raise(new IdentityNotInitializedError());
|
|
245
240
|
|
|
246
241
|
assert(!this._options.readOnly, 'PartyFactory is read-only');
|
|
247
|
-
assert(identity.identityGenesis, 'IdentityGenesis must exist');
|
|
248
|
-
assert(identity.deviceKeyChain, 'Device KeyChain must exist');
|
|
249
242
|
|
|
250
243
|
const partyKey = await identity.keyring.createKeyRecord({ type: KeyType.PARTY });
|
|
251
244
|
const party = await this.constructParty(partyKey.publicKey);
|
|
@@ -257,7 +250,7 @@ export class PartyFactory {
|
|
|
257
250
|
|
|
258
251
|
// PartyGenesis (self-signed by Party).
|
|
259
252
|
await party.processor.writeHaloMessage(createPartyGenesisMessage(
|
|
260
|
-
identity.
|
|
253
|
+
identity.keyring,
|
|
261
254
|
partyKey,
|
|
262
255
|
writableFeed.key,
|
|
263
256
|
partyKey)
|
|
@@ -265,7 +258,7 @@ export class PartyFactory {
|
|
|
265
258
|
|
|
266
259
|
// KeyAdmit (IdentityGenesis in an Envelope signed by Party).
|
|
267
260
|
await party.processor.writeHaloMessage(createEnvelopeMessage(
|
|
268
|
-
identity.
|
|
261
|
+
identity.keyring,
|
|
269
262
|
partyKey.publicKey,
|
|
270
263
|
wrapMessage(identity.identityGenesis),
|
|
271
264
|
[partyKey]
|
|
@@ -273,7 +266,7 @@ export class PartyFactory {
|
|
|
273
266
|
|
|
274
267
|
// FeedAdmit (signed by the Device KeyChain).
|
|
275
268
|
await party.processor.writeHaloMessage(createFeedAdmitMessage(
|
|
276
|
-
identity.
|
|
269
|
+
identity.keyring,
|
|
277
270
|
partyKey.publicKey,
|
|
278
271
|
writableFeed.key,
|
|
279
272
|
[identity.deviceKeyChain]
|
|
@@ -282,7 +275,7 @@ export class PartyFactory {
|
|
|
282
275
|
// IdentityInfo in an Envelope signed by the Device KeyChain.
|
|
283
276
|
if (identity.identityInfo) {
|
|
284
277
|
await party.processor.writeHaloMessage(createEnvelopeMessage(
|
|
285
|
-
identity.
|
|
278
|
+
identity.keyring,
|
|
286
279
|
partyKey.publicKey,
|
|
287
280
|
wrapMessage(identity.identityInfo),
|
|
288
281
|
[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
|
|
|
@@ -74,10 +74,11 @@ export class PartyManager {
|
|
|
74
74
|
|
|
75
75
|
let partyKeys = this._metadataStore.parties.map(party => party.key).filter(boolGuard);
|
|
76
76
|
|
|
77
|
+
// Identity may be undefined, for example, on the first start.
|
|
77
78
|
const identity = this._identityProvider();
|
|
78
79
|
|
|
79
80
|
// TODO(telackey): Does it make any sense to load other parties if we don't have an HALO?
|
|
80
|
-
if (identity
|
|
81
|
+
if (identity) {
|
|
81
82
|
partyKeys = partyKeys.filter(partyKey => !partyKey.equals(identity.identityKey!.publicKey));
|
|
82
83
|
}
|
|
83
84
|
|