@dxos/echo-db 2.33.3-dev.1cc72521 → 2.33.3-dev.762b75aa
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-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/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/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 +12 -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/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/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 +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/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-party.ts +1 -2
- 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/data-party.test.ts +212 -0
- package/src/parties/data-party.ts +1 -2
- package/src/parties/party-factory.ts +14 -20
- package/src/parties/party-manager.test.ts +8 -42
- package/src/parties/party-manager.ts +4 -3
- package/src/pipeline/party-core.test.ts +2 -4
- package/src/pipeline/party-processor.ts +0 -13
- package/src/protocol/credentials-signer.ts +9 -9
- package/src/protocol/identity-credentials.ts +78 -0
- package/src/protocol/party-protocol-factory.ts +13 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/echo-db",
|
|
3
|
-
"version": "2.33.3-dev.
|
|
3
|
+
"version": "2.33.3-dev.762b75aa",
|
|
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.3-dev.
|
|
14
|
-
"@dxos/codec-protobuf": "2.33.3-dev.
|
|
15
|
-
"@dxos/credentials": "2.33.3-dev.
|
|
16
|
-
"@dxos/crypto": "2.33.3-dev.
|
|
17
|
-
"@dxos/debug": "2.33.3-dev.
|
|
18
|
-
"@dxos/echo-protocol": "2.33.3-dev.
|
|
19
|
-
"@dxos/feed-store": "2.33.3-dev.
|
|
20
|
-
"@dxos/mesh-protocol": "2.33.3-dev.
|
|
21
|
-
"@dxos/model-factory": "2.33.3-dev.
|
|
22
|
-
"@dxos/network-manager": "2.33.3-dev.
|
|
23
|
-
"@dxos/object-model": "2.33.3-dev.
|
|
24
|
-
"@dxos/protocol-plugin-presence": "2.33.3-dev.
|
|
25
|
-
"@dxos/protocol-plugin-replicator": "2.33.3-dev.
|
|
26
|
-
"@dxos/protocols": "2.33.3-dev.
|
|
27
|
-
"@dxos/random-access-multi-storage": "2.33.3-dev.
|
|
28
|
-
"@dxos/util": "2.33.3-dev.
|
|
13
|
+
"@dxos/async": "2.33.3-dev.762b75aa",
|
|
14
|
+
"@dxos/codec-protobuf": "2.33.3-dev.762b75aa",
|
|
15
|
+
"@dxos/credentials": "2.33.3-dev.762b75aa",
|
|
16
|
+
"@dxos/crypto": "2.33.3-dev.762b75aa",
|
|
17
|
+
"@dxos/debug": "2.33.3-dev.762b75aa",
|
|
18
|
+
"@dxos/echo-protocol": "2.33.3-dev.762b75aa",
|
|
19
|
+
"@dxos/feed-store": "2.33.3-dev.762b75aa",
|
|
20
|
+
"@dxos/mesh-protocol": "2.33.3-dev.762b75aa",
|
|
21
|
+
"@dxos/model-factory": "2.33.3-dev.762b75aa",
|
|
22
|
+
"@dxos/network-manager": "2.33.3-dev.762b75aa",
|
|
23
|
+
"@dxos/object-model": "2.33.3-dev.762b75aa",
|
|
24
|
+
"@dxos/protocol-plugin-presence": "2.33.3-dev.762b75aa",
|
|
25
|
+
"@dxos/protocol-plugin-replicator": "2.33.3-dev.762b75aa",
|
|
26
|
+
"@dxos/protocols": "2.33.3-dev.762b75aa",
|
|
27
|
+
"@dxos/random-access-multi-storage": "2.33.3-dev.762b75aa",
|
|
28
|
+
"@dxos/util": "2.33.3-dev.762b75aa",
|
|
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-party.ts
CHANGED
|
@@ -163,8 +163,7 @@ export class HaloParty {
|
|
|
163
163
|
this._networkManager,
|
|
164
164
|
this._feedProvider,
|
|
165
165
|
peerId,
|
|
166
|
-
createCredentialsProvider(this._credentialsSigner, this._partyCore.key, writeFeed.key)
|
|
167
|
-
this._partyCore.processor.getActiveFeedSet()
|
|
166
|
+
createCredentialsProvider(this._credentialsSigner, this._partyCore.key, writeFeed.key)
|
|
168
167
|
);
|
|
169
168
|
|
|
170
169
|
// Replication.
|
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
|
));
|
|
@@ -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([
|