@dxos/echo-db 2.33.5-dev.4dcc5349 → 2.33.5-dev.61c93034
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/api/selection/queries.d.ts.map +1 -1
- package/dist/src/api/selection/queries.js +13 -17
- package/dist/src/api/selection/queries.js.map +1 -1
- package/dist/src/api/selection/selection.d.ts.map +1 -1
- package/dist/src/api/selection/selection.js +1 -3
- package/dist/src/api/selection/selection.js.map +1 -1
- package/dist/src/api/selection/selection.test.js +4 -12
- package/dist/src/api/selection/selection.test.js.map +1 -1
- package/dist/src/database/item-demuxer.d.ts +1 -1
- package/dist/src/database/item-demuxer.d.ts.map +1 -1
- package/dist/src/database/item-demuxer.js +3 -5
- package/dist/src/database/item-demuxer.js.map +1 -1
- package/dist/src/database/item-manager.js +2 -2
- package/dist/src/database/item-manager.js.map +1 -1
- package/dist/src/database/testing.js +1 -1
- package/dist/src/database/testing.js.map +1 -1
- package/dist/src/halo/halo-party.d.ts.map +1 -1
- package/dist/src/halo/halo-party.js +3 -1
- package/dist/src/halo/halo-party.js.map +1 -1
- package/dist/src/halo/identity.js +2 -2
- package/dist/src/halo/identity.js.map +1 -1
- package/dist/src/halo/party-opener.d.ts +1 -1
- package/dist/src/halo/party-opener.d.ts.map +1 -1
- package/dist/src/halo/party-opener.js +2 -2
- package/dist/src/halo/party-opener.js.map +1 -1
- package/dist/src/halo/preferences.d.ts.map +1 -1
- package/dist/src/halo/preferences.js +7 -10
- package/dist/src/halo/preferences.js.map +1 -1
- package/dist/src/invitations/greeting-protocol-provider.d.ts.map +1 -1
- package/dist/src/invitations/greeting-protocol-provider.js +5 -9
- package/dist/src/invitations/greeting-protocol-provider.js.map +1 -1
- package/dist/src/invitations/greeting-responder.d.ts.map +1 -1
- package/dist/src/invitations/greeting-responder.js +8 -12
- package/dist/src/invitations/greeting-responder.js.map +1 -1
- package/dist/src/invitations/offline-invitation-claimer.d.ts.map +1 -1
- package/dist/src/invitations/offline-invitation-claimer.js +5 -7
- 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 +3 -1
- package/dist/src/parties/data-party.js.map +1 -1
- package/dist/src/pipeline/message-selector.d.ts +1 -1
- package/dist/src/pipeline/message-selector.d.ts.map +1 -1
- package/dist/src/pipeline/message-selector.js +29 -32
- package/dist/src/pipeline/message-selector.js.map +1 -1
- package/dist/src/pipeline/party-core.test.js +2 -1
- package/dist/src/pipeline/party-core.test.js.map +1 -1
- package/dist/src/protocol/auth-plugin.d.ts +1 -1
- package/dist/src/protocol/auth-plugin.d.ts.map +1 -1
- package/dist/src/protocol/auth-plugin.js +1 -3
- package/dist/src/protocol/auth-plugin.js.map +1 -1
- package/dist/src/protocol/authenticator.d.ts +2 -2
- package/dist/src/protocol/authenticator.d.ts.map +1 -1
- package/dist/src/protocol/authenticator.js +12 -16
- package/dist/src/protocol/authenticator.js.map +1 -1
- package/dist/src/protocol/halo-recovery-plugin.d.ts +1 -1
- package/dist/src/protocol/halo-recovery-plugin.d.ts.map +1 -1
- package/dist/src/protocol/halo-recovery-plugin.js +1 -3
- package/dist/src/protocol/halo-recovery-plugin.js.map +1 -1
- package/dist/src/protocol/identity-credentials.d.ts +2 -2
- package/dist/src/protocol/identity-credentials.d.ts.map +1 -1
- package/dist/src/protocol/identity-credentials.js +4 -4
- package/dist/src/protocol/identity-credentials.js.map +1 -1
- package/dist/src/protocol/offline-invitation-plugin.d.ts +1 -1
- package/dist/src/protocol/offline-invitation-plugin.d.ts.map +1 -1
- package/dist/src/protocol/offline-invitation-plugin.js +1 -3
- package/dist/src/protocol/offline-invitation-plugin.js.map +1 -1
- package/dist/src/protocol/party-protocol-factory.d.ts +1 -14
- package/dist/src/protocol/party-protocol-factory.d.ts.map +1 -1
- package/dist/src/protocol/party-protocol-factory.js +2 -55
- package/dist/src/protocol/party-protocol-factory.js.map +1 -1
- package/dist/src/protocol/replicator-plugin.d.ts +7 -0
- package/dist/src/protocol/replicator-plugin.d.ts.map +1 -0
- package/dist/src/protocol/replicator-plugin.js +36 -0
- package/dist/src/protocol/replicator-plugin.js.map +1 -0
- package/dist/src/snapshots/snapshot-generator.d.ts +1 -1
- package/dist/src/snapshots/snapshot-generator.d.ts.map +1 -1
- package/dist/src/snapshots/snapshot-generator.js +13 -15
- package/dist/src/snapshots/snapshot-generator.js.map +1 -1
- package/dist/src/testing/benchmark.test.d.ts +2 -0
- package/dist/src/testing/benchmark.test.d.ts.map +1 -0
- package/dist/src/testing/benchmark.test.js +25 -0
- package/dist/src/testing/benchmark.test.js.map +1 -0
- package/dist/src/testing/testing-factories.js +2 -2
- package/dist/src/testing/testing-factories.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +18 -18
- package/src/api/selection/queries.ts +14 -18
- package/src/api/selection/selection.test.ts +4 -12
- package/src/api/selection/selection.ts +1 -7
- package/src/database/item-demuxer.ts +2 -4
- package/src/database/item-manager.ts +2 -2
- package/src/database/testing.ts +1 -1
- package/src/halo/halo-party.ts +2 -1
- package/src/halo/identity.ts +2 -2
- package/src/halo/party-opener.ts +2 -2
- package/src/halo/preferences.ts +7 -10
- package/src/invitations/greeting-protocol-provider.ts +5 -9
- package/src/invitations/greeting-responder.ts +9 -13
- package/src/invitations/offline-invitation-claimer.ts +5 -7
- package/src/parties/data-party.ts +2 -1
- package/src/pipeline/message-selector.ts +30 -36
- package/src/pipeline/party-core.test.ts +4 -7
- package/src/protocol/auth-plugin.ts +1 -3
- package/src/protocol/authenticator.ts +27 -31
- package/src/protocol/halo-recovery-plugin.ts +4 -6
- package/src/protocol/identity-credentials.ts +4 -4
- package/src/protocol/offline-invitation-plugin.ts +4 -6
- package/src/protocol/party-protocol-factory.ts +3 -56
- package/src/protocol/replicator-plugin.ts +37 -0
- package/src/snapshots/snapshot-generator.ts +12 -16
- package/src/testing/benchmark.test.ts +30 -0
- package/src/testing/testing-factories.ts +2 -2
|
@@ -24,9 +24,7 @@ const modelFactory = new ModelFactory().registerModel(ObjectModel);
|
|
|
24
24
|
|
|
25
25
|
const createModel = (id: ItemID) => modelFactory.createModel(ObjectModel.meta.type, id, {}, PublicKey.random());
|
|
26
26
|
|
|
27
|
-
const createItem = (id: ItemID, type: ItemType, parent?: Item<any>) =>
|
|
28
|
-
return new Item(null as any, id, type, createModel(id), undefined, parent);
|
|
29
|
-
};
|
|
27
|
+
const createItem = (id: ItemID, type: ItemType, parent?: Item<any>) => new Item(null as any, id, type, createModel(id), undefined, parent);
|
|
30
28
|
|
|
31
29
|
const createLink = (id: ItemID, type: ItemType, source: Item<any>, target: Item<any>) => {
|
|
32
30
|
const link = new Link(null as any, id, type, createModel(id), {
|
|
@@ -262,17 +260,11 @@ describe('Selection', () => {
|
|
|
262
260
|
test('complex reducer', () => {
|
|
263
261
|
const query = createReducer({ numItems: 0, numLinks: 0 })
|
|
264
262
|
.filter({ type: ITEM_ORG })
|
|
265
|
-
.call((items: Item[], { numItems, ...rest }) => {
|
|
266
|
-
return { ...rest, numItems: numItems + items.length, stage: 'a' };
|
|
267
|
-
})
|
|
263
|
+
.call((items: Item[], { numItems, ...rest }) => ({ ...rest, numItems: numItems + items.length, stage: 'a' }))
|
|
268
264
|
.children({ type: ITEM_PROJECT })
|
|
269
|
-
.call((items: Item[], { numItems, ...rest }) => {
|
|
270
|
-
return { ...rest, numItems: numItems + items.length, stage: 'b' };
|
|
271
|
-
})
|
|
265
|
+
.call((items: Item[], { numItems, ...rest }) => ({ ...rest, numItems: numItems + items.length, stage: 'b' }))
|
|
272
266
|
.links({ type: LINK_MEMBER })
|
|
273
|
-
.call((links: Link[], { numLinks, ...rest }) => {
|
|
274
|
-
return { ...rest, numLinks: numLinks + links.length, stage: 'c' };
|
|
275
|
-
})
|
|
267
|
+
.call((links: Link[], { numLinks, ...rest }) => ({ ...rest, numLinks: numLinks + links.length, stage: 'c' }))
|
|
276
268
|
.target()
|
|
277
269
|
.exec();
|
|
278
270
|
|
|
@@ -57,13 +57,7 @@ export const createSelection = <R>(
|
|
|
57
57
|
* @param update
|
|
58
58
|
* @param value Initial reducer value.
|
|
59
59
|
*/
|
|
60
|
-
export const createItemSelection = <R>(
|
|
61
|
-
root: Item<any>,
|
|
62
|
-
update: Event<Entity[]>,
|
|
63
|
-
value: R
|
|
64
|
-
): Selection<Item<any>, R> => {
|
|
65
|
-
return new Selection(() => [[root], value], update, root, value !== undefined);
|
|
66
|
-
};
|
|
60
|
+
export const createItemSelection = <R>(root: Item<any>, update: Event<Entity[]>, value: R): Selection<Item<any>, R> => new Selection(() => [[root], value], update, root, value !== undefined);
|
|
67
61
|
|
|
68
62
|
/**
|
|
69
63
|
* Selections are used to construct database subscriptions.
|
|
@@ -10,7 +10,6 @@ import { failUndefined } from '@dxos/debug';
|
|
|
10
10
|
import { DatabaseSnapshot, IEchoStream, ItemID, ItemSnapshot, LinkSnapshot } from '@dxos/echo-protocol';
|
|
11
11
|
import { createWritable } from '@dxos/feed-store';
|
|
12
12
|
import { Model, ModelFactory, ModelMessage } from '@dxos/model-factory';
|
|
13
|
-
import { jsonReplacer } from '@dxos/util';
|
|
14
13
|
|
|
15
14
|
import { Entity, Item, Link } from '../api';
|
|
16
15
|
import { ItemManager, ModelConstructionOptions } from './item-manager';
|
|
@@ -46,7 +45,6 @@ export class ItemDemuxer {
|
|
|
46
45
|
// TODO(burdon): Factor out.
|
|
47
46
|
// TODO(burdon): Should this implement some "back-pressure" (hints) to the PartyProcessor?
|
|
48
47
|
return createWritable<IEchoStream>(async (message: IEchoStream) => {
|
|
49
|
-
log('Reading:', JSON.stringify(message, jsonReplacer));
|
|
50
48
|
const { data: { itemId, genesis, itemMutation, mutation, snapshot }, meta } = message;
|
|
51
49
|
assert(itemId);
|
|
52
50
|
|
|
@@ -187,7 +185,7 @@ export class ItemDemuxer {
|
|
|
187
185
|
* Sort based on parents.
|
|
188
186
|
* @param items
|
|
189
187
|
*/
|
|
190
|
-
export
|
|
188
|
+
export const sortItemsTopologically = (items: ItemSnapshot[]): ItemSnapshot[] => {
|
|
191
189
|
const snapshots: ItemSnapshot[] = [];
|
|
192
190
|
const seenIds = new Set<ItemID>();
|
|
193
191
|
|
|
@@ -206,4 +204,4 @@ export function sortItemsTopologically (items: ItemSnapshot[]): ItemSnapshot[] {
|
|
|
206
204
|
}
|
|
207
205
|
|
|
208
206
|
return snapshots;
|
|
209
|
-
}
|
|
207
|
+
};
|
|
@@ -375,7 +375,7 @@ export class ItemManager {
|
|
|
375
375
|
/**
|
|
376
376
|
* Returns a new event that groups all of the updates emitted during single tick into a single event emission.
|
|
377
377
|
*/
|
|
378
|
-
|
|
378
|
+
const debounceEntityUpdateEvent = (event: Event<Entity<any>>): Event<Entity<any>[]> => {
|
|
379
379
|
const debouncedEvent = new Event<Entity<any>[]>();
|
|
380
380
|
|
|
381
381
|
let firing = false;
|
|
@@ -395,4 +395,4 @@ function debounceEntityUpdateEvent (event: Event<Entity<any>>): Event<Entity<any
|
|
|
395
395
|
}));
|
|
396
396
|
|
|
397
397
|
return debouncedEvent;
|
|
398
|
-
}
|
|
398
|
+
};
|
package/src/database/testing.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { FeedDatabaseBackend, RemoteDatabaseBackend } from './database-backend';
|
|
|
15
15
|
|
|
16
16
|
export const createInMemoryDatabase = async (modelFactory: ModelFactory) => {
|
|
17
17
|
const feed = new MockFeedWriter<EchoEnvelope>();
|
|
18
|
-
const inboundStream = new Readable({ read () {}, objectMode: true });
|
|
18
|
+
const inboundStream = new Readable({ read: () => {}, objectMode: true });
|
|
19
19
|
feed.written.on(([data, meta]) => inboundStream.push({ data, meta: { ...meta, memberKey: PublicKey.random(), timeframe: new Timeframe([[meta.feedKey, meta.seq]]) } }));
|
|
20
20
|
|
|
21
21
|
const database = new Database(
|
package/src/halo/halo-party.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { PARTY_ITEM_TYPE } from '../parties';
|
|
|
17
17
|
import { PartyFeedProvider, PartyProtocolFactory, PartyCore, PartyOptions } from '../pipeline';
|
|
18
18
|
import { createAuthenticator, createAuthPlugin, createCredentialsProvider, createHaloRecoveryPlugin } from '../protocol';
|
|
19
19
|
import { CredentialsSigner } from '../protocol/credentials-signer';
|
|
20
|
+
import { createReplicatorPlugin } from '../protocol/replicator-plugin';
|
|
20
21
|
import { SnapshotStore } from '../snapshots';
|
|
21
22
|
import { ContactManager } from './contact-manager';
|
|
22
23
|
import { Preferences } from './preferences';
|
|
@@ -161,13 +162,13 @@ export class HaloParty {
|
|
|
161
162
|
this._protocol = new PartyProtocolFactory(
|
|
162
163
|
this._partyCore.key,
|
|
163
164
|
this._networkManager,
|
|
164
|
-
this._feedProvider,
|
|
165
165
|
peerId,
|
|
166
166
|
createCredentialsProvider(this._credentialsSigner, this._partyCore.key, writeFeed.key)
|
|
167
167
|
);
|
|
168
168
|
|
|
169
169
|
// Replication.
|
|
170
170
|
await this._protocol.start([
|
|
171
|
+
createReplicatorPlugin(this._feedProvider),
|
|
171
172
|
createAuthPlugin(createAuthenticator(this._partyCore.processor, this._credentialsSigner), peerId),
|
|
172
173
|
createHaloRecoveryPlugin(this._credentialsSigner.getIdentityKey().publicKey, this._invitationManager, peerId)
|
|
173
174
|
]);
|
package/src/halo/identity.ts
CHANGED
|
@@ -102,7 +102,7 @@ export class Identity implements IdentityCredentials {
|
|
|
102
102
|
|
|
103
103
|
export type IdentityProvider = () => Identity | undefined;
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
const getDeviceKeyChainFromHalo = (halo: HaloParty, deviceKey: KeyRecord) => {
|
|
106
106
|
try {
|
|
107
107
|
return Keyring.buildKeyChain(
|
|
108
108
|
deviceKey.publicKey,
|
|
@@ -113,4 +113,4 @@ function getDeviceKeyChainFromHalo (halo: HaloParty, deviceKey: KeyRecord) {
|
|
|
113
113
|
log('Unable to locate device KeyChain:', err);
|
|
114
114
|
throw err;
|
|
115
115
|
}
|
|
116
|
-
}
|
|
116
|
+
};
|
package/src/halo/party-opener.ts
CHANGED
|
@@ -14,7 +14,7 @@ const log = debug('dxos:echo-db:party-opener');
|
|
|
14
14
|
/**
|
|
15
15
|
* Automatically adds, opens, and clothes parties from HALO preferences.
|
|
16
16
|
*/
|
|
17
|
-
export
|
|
17
|
+
export const autoPartyOpener = (preferences: Preferences, partyManager: PartyManager): Unsubscribe => {
|
|
18
18
|
const subs = new SubscriptionGroup();
|
|
19
19
|
|
|
20
20
|
subs.push(preferences.subscribeToJoinedPartyList(async values => {
|
|
@@ -46,4 +46,4 @@ export function autoPartyOpener (preferences: Preferences, partyManager: PartyMa
|
|
|
46
46
|
}));
|
|
47
47
|
|
|
48
48
|
return () => subs.unsubscribe();
|
|
49
|
-
}
|
|
49
|
+
};
|
package/src/halo/preferences.ts
CHANGED
|
@@ -159,16 +159,13 @@ export class Preferences {
|
|
|
159
159
|
subscribeToJoinedPartyList (callback: (parties: JoinedParty[]) => void): () => void {
|
|
160
160
|
const database = this._getDatabase() ?? raise(new IdentityNotInitializedError());
|
|
161
161
|
|
|
162
|
-
const converter = (partyDesc: Item<any>) => {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
} as KeyHint))
|
|
170
|
-
};
|
|
171
|
-
};
|
|
162
|
+
const converter = (partyDesc: Item<any>) => ({
|
|
163
|
+
partyKey: PublicKey.from(partyDesc.model.get('publicKey')),
|
|
164
|
+
keyHints: Object.values(partyDesc.model.get('hints')).map((hint: any) => ({
|
|
165
|
+
...hint,
|
|
166
|
+
publicKey: PublicKey.from(hint.publicKey)
|
|
167
|
+
} as KeyHint))
|
|
168
|
+
});
|
|
172
169
|
|
|
173
170
|
const result = database.select({ type: HALO_PARTY_DESCRIPTOR_TYPE }).exec();
|
|
174
171
|
|
|
@@ -14,12 +14,8 @@ import { protocolFactory } from '@dxos/network-manager';
|
|
|
14
14
|
*/
|
|
15
15
|
// TODO(burdon): When closed?
|
|
16
16
|
// TODO(dboreham): Write a test to check resources are released (no resource leaks).
|
|
17
|
-
export const greetingProtocolProvider = (rendezvousKey: any, peerId: Buffer | Uint8Array, protocolPlugins: any[]) => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
session: { peerId: keyToString(peerId) },
|
|
23
|
-
plugins: protocolPlugins
|
|
24
|
-
});
|
|
25
|
-
};
|
|
17
|
+
export const greetingProtocolProvider = (rendezvousKey: any, peerId: Buffer | Uint8Array, protocolPlugins: any[]) => protocolFactory({
|
|
18
|
+
getTopics: () => [rendezvousKey],
|
|
19
|
+
session: { peerId: keyToString(peerId) },
|
|
20
|
+
plugins: protocolPlugins
|
|
21
|
+
});
|
|
@@ -243,19 +243,15 @@ export class GreetingResponder {
|
|
|
243
243
|
_gatherHints (): KeyHint[] {
|
|
244
244
|
assert(this._state === GreetingState.SUCCEEDED);
|
|
245
245
|
|
|
246
|
-
const memberKeys = this._partyProcessor.memberKeys.map(publicKey => {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
publicKey,
|
|
256
|
-
type: KeyType.FEED
|
|
257
|
-
};
|
|
258
|
-
});
|
|
246
|
+
const memberKeys = this._partyProcessor.memberKeys.map((publicKey) => ({
|
|
247
|
+
publicKey,
|
|
248
|
+
type: KeyType.UNKNOWN
|
|
249
|
+
}));
|
|
250
|
+
|
|
251
|
+
const memberFeeds = this._partyProcessor.feedKeys.map((publicKey) => ({
|
|
252
|
+
publicKey,
|
|
253
|
+
type: KeyType.FEED
|
|
254
|
+
}));
|
|
259
255
|
|
|
260
256
|
return [...memberKeys, ...memberFeeds];
|
|
261
257
|
}
|
|
@@ -170,19 +170,17 @@ export class OfflineInvitationClaimer {
|
|
|
170
170
|
|
|
171
171
|
// The secretProvider should provide an `Auth` message signed directly by the Identity key.
|
|
172
172
|
static createSecretProvider (credentials: CredentialsSigner): SecretProvider {
|
|
173
|
-
return async (info?: SecretInfo) =>
|
|
174
|
-
|
|
175
|
-
/* The signed portion of the Auth message includes the ID and authNonce provided
|
|
173
|
+
return async (info?: SecretInfo) => Buffer.from(codec.encode(
|
|
174
|
+
/* The signed portion of the Auth message includes the ID and authNonce provided
|
|
176
175
|
* by the `info` object. These values will be validated on the other end.
|
|
177
176
|
*/
|
|
178
|
-
|
|
179
|
-
|
|
177
|
+
createAuthMessage(
|
|
178
|
+
credentials.signer,
|
|
180
179
|
info!.id.value,
|
|
181
180
|
credentials.getIdentityKey(),
|
|
182
181
|
credentials.getDeviceSigningKeys(),
|
|
183
182
|
undefined,
|
|
184
183
|
info!.authNonce.value)
|
|
185
|
-
|
|
186
|
-
};
|
|
184
|
+
));
|
|
187
185
|
}
|
|
188
186
|
}
|
|
@@ -19,6 +19,7 @@ import { InvitationFactory } from '../invitations';
|
|
|
19
19
|
import { PartyFeedProvider, PartyProtocolFactory, PartyCore, PartyOptions } from '../pipeline';
|
|
20
20
|
import { createAuthPlugin, createOfflineInvitationPlugin, createAuthenticator, createCredentialsProvider } from '../protocol';
|
|
21
21
|
import { CredentialsSigner } from '../protocol/credentials-signer';
|
|
22
|
+
import { createReplicatorPlugin } from '../protocol/replicator-plugin';
|
|
22
23
|
import { SnapshotStore } from '../snapshots';
|
|
23
24
|
import { CONTACT_DEBOUNCE_INTERVAL } from './party-manager';
|
|
24
25
|
|
|
@@ -173,12 +174,12 @@ export class DataParty {
|
|
|
173
174
|
this._protocol = new PartyProtocolFactory(
|
|
174
175
|
this._partyCore.key,
|
|
175
176
|
this._networkManager,
|
|
176
|
-
this._feedProvider,
|
|
177
177
|
deviceKey.publicKey,
|
|
178
178
|
createCredentialsProvider(this._credentialsSigner, this._partyCore.key, writeFeed.key)
|
|
179
179
|
);
|
|
180
180
|
|
|
181
181
|
await this._protocol.start([
|
|
182
|
+
createReplicatorPlugin(this._feedProvider),
|
|
182
183
|
createAuthPlugin(createAuthenticator(this._partyCore.processor, this._credentialsSigner), deviceKey.publicKey),
|
|
183
184
|
createOfflineInvitationPlugin(this._invitationManager, deviceKey.publicKey)
|
|
184
185
|
]);
|
|
@@ -23,47 +23,41 @@ const log = debug('dxos:echo-db:message-selector');
|
|
|
23
23
|
* @param partyProcessor
|
|
24
24
|
* @param timeframeClock
|
|
25
25
|
*/
|
|
26
|
-
export
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const { data: { echo } } = candidates[i];
|
|
35
|
-
const feedKey = PublicKey.from(candidates[i].key);
|
|
36
|
-
if (!echo) {
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
26
|
+
export const createMessageSelector = (partyProcessor: PartyProcessor, timeframeClock: TimeframeClock): MessageSelector => candidates => {
|
|
27
|
+
// Check ECHO message candidates first since they are less expensive than HALO cancidates.
|
|
28
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
29
|
+
const { data: { echo } } = candidates[i];
|
|
30
|
+
const feedKey = PublicKey.from(candidates[i].key);
|
|
31
|
+
if (!echo) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
39
34
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
assert(echo.timeframe);
|
|
36
|
+
if (partyProcessor.isFeedAdmitted(feedKey) && !timeframeClock.hasGaps(echo.timeframe)) {
|
|
37
|
+
return i;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Check HALO message candidates.
|
|
42
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
43
|
+
const { data: { halo } } = candidates[i];
|
|
44
|
+
const feedKey = PublicKey.from(candidates[i].key);
|
|
45
|
+
if (!halo) {
|
|
46
|
+
continue;
|
|
44
47
|
}
|
|
45
48
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const feedKey = PublicKey.from(candidates[i].key);
|
|
50
|
-
if (!halo) {
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
49
|
+
if (partyProcessor.isFeedAdmitted(feedKey)) {
|
|
50
|
+
return i;
|
|
51
|
+
}
|
|
53
52
|
|
|
54
|
-
|
|
53
|
+
if (partyProcessor.genesisRequired) {
|
|
54
|
+
// TODO(telackey): Add check that this is for the right Party.
|
|
55
|
+
if (getPartyCredentialMessageType(halo) === PartyCredential.Type.PARTY_GENESIS) {
|
|
55
56
|
return i;
|
|
56
57
|
}
|
|
57
|
-
|
|
58
|
-
if (partyProcessor.genesisRequired) {
|
|
59
|
-
// TODO(telackey): Add check that this is for the right Party.
|
|
60
|
-
if (getPartyCredentialMessageType(halo) === PartyCredential.Type.PARTY_GENESIS) {
|
|
61
|
-
return i;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
58
|
}
|
|
59
|
+
}
|
|
65
60
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
61
|
+
// Not ready for this message yet.
|
|
62
|
+
log('Skipping...');
|
|
63
|
+
};
|
|
@@ -16,7 +16,8 @@ import { ObjectModel } from '@dxos/object-model';
|
|
|
16
16
|
import { createStorage, StorageType } from '@dxos/random-access-multi-storage';
|
|
17
17
|
import { afterTest } from '@dxos/testutils';
|
|
18
18
|
|
|
19
|
-
import { MetadataStore, PartyFeedProvider
|
|
19
|
+
import { MetadataStore, PartyFeedProvider } from '../pipeline';
|
|
20
|
+
import { createReplicatorPlugin } from '../protocol/replicator-plugin';
|
|
20
21
|
import { SnapshotStore } from '../snapshots';
|
|
21
22
|
import { PartyCore } from './party-core';
|
|
22
23
|
|
|
@@ -286,12 +287,8 @@ describe('PartyCore', () => {
|
|
|
286
287
|
afterTest(async () => party2.close());
|
|
287
288
|
|
|
288
289
|
createTestProtocolPair(
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
).createPlugins().map(r => r.createExtension()),
|
|
292
|
-
new ReplicatorProtocolPluginFactory(
|
|
293
|
-
partyFeedProvider
|
|
294
|
-
).createPlugins().map(r => r.createExtension())
|
|
290
|
+
[createReplicatorPlugin(peer1.partyFeedProvider).createExtension()],
|
|
291
|
+
[createReplicatorPlugin(partyFeedProvider).createExtension()]
|
|
295
292
|
);
|
|
296
293
|
|
|
297
294
|
const item1 = await peer1.party.database.createItem();
|
|
@@ -9,6 +9,4 @@ import { Replicator } from '@dxos/protocol-plugin-replicator';
|
|
|
9
9
|
/**
|
|
10
10
|
* Creates authenticator network-protocol plugin that guards access to the replicator.
|
|
11
11
|
*/
|
|
12
|
-
export
|
|
13
|
-
return new AuthPlugin(peerId.asBuffer(), authenticator, [Replicator.extension]);
|
|
14
|
-
}
|
|
12
|
+
export const createAuthPlugin = (authenticator: Authenticator, peerId: PublicKey) => new AuthPlugin(peerId.asBuffer(), authenticator, [Replicator.extension]);
|
|
@@ -12,19 +12,17 @@ import { CredentialsSigner } from './credentials-signer';
|
|
|
12
12
|
|
|
13
13
|
const log = debug('dxos:echo-db:authenticator');
|
|
14
14
|
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
});
|
|
27
|
-
}
|
|
15
|
+
export const createAuthenticator = (partyProcessor: PartyProcessor, credentialsSigner: CredentialsSigner): Authenticator => new PartyAuthenticator(partyProcessor.state, async auth => {
|
|
16
|
+
if (auth.feedAdmit && auth.feedKey && !partyProcessor.isFeedAdmitted(auth.feedKey)) {
|
|
17
|
+
log(`Admitting feed of authenticated member: ${auth.feedKey}`);
|
|
18
|
+
await partyProcessor.writeHaloMessage(createEnvelopeMessage(
|
|
19
|
+
credentialsSigner.signer,
|
|
20
|
+
partyProcessor.partyKey,
|
|
21
|
+
auth.feedAdmit,
|
|
22
|
+
[credentialsSigner.getDeviceSigningKeys()]
|
|
23
|
+
));
|
|
24
|
+
}
|
|
25
|
+
});
|
|
28
26
|
|
|
29
27
|
export interface CredentialsProvider {
|
|
30
28
|
/**
|
|
@@ -33,24 +31,22 @@ export interface CredentialsProvider {
|
|
|
33
31
|
get (): Buffer
|
|
34
32
|
}
|
|
35
33
|
|
|
36
|
-
export
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
export const createCredentialsProvider = (credentialsSigner: CredentialsSigner, partyKey: PartyKey, feedKey: FeedKey): CredentialsProvider => ({
|
|
35
|
+
get: () => {
|
|
36
|
+
const authMessage = createAuthMessage(
|
|
37
|
+
credentialsSigner.signer,
|
|
38
|
+
partyKey,
|
|
39
|
+
credentialsSigner.getIdentityKey(),
|
|
40
|
+
credentialsSigner.getDeviceSigningKeys(),
|
|
41
|
+
feedKey,
|
|
42
|
+
undefined,
|
|
43
|
+
createFeedAdmitMessage(
|
|
40
44
|
credentialsSigner.signer,
|
|
41
45
|
partyKey,
|
|
42
|
-
credentialsSigner.getIdentityKey(),
|
|
43
|
-
credentialsSigner.getDeviceSigningKeys(),
|
|
44
46
|
feedKey,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
)
|
|
52
|
-
);
|
|
53
|
-
return Buffer.from(codec.encode(authMessage));
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
}
|
|
47
|
+
[feedKey, credentialsSigner.getDeviceSigningKeys()]
|
|
48
|
+
)
|
|
49
|
+
);
|
|
50
|
+
return Buffer.from(codec.encode(authMessage));
|
|
51
|
+
}
|
|
52
|
+
});
|
|
@@ -12,9 +12,7 @@ import { HaloRecoveryInitiator, InvitationFactory } from '../invitations';
|
|
|
12
12
|
* Plugin is intended to be used in HALO party swarm.
|
|
13
13
|
*
|
|
14
14
|
*/
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
);
|
|
20
|
-
}
|
|
15
|
+
export const createHaloRecoveryPlugin = (identityKey: PublicKey, invitationFactory: InvitationFactory, peerId: PublicKey) => new GreetingCommandPlugin(
|
|
16
|
+
peerId.asBuffer(),
|
|
17
|
+
HaloRecoveryInitiator.createHaloInvitationClaimHandler(identityKey, invitationFactory)
|
|
18
|
+
);
|
|
@@ -25,7 +25,7 @@ export interface IdentityCredentials {
|
|
|
25
25
|
|
|
26
26
|
export type IdentityCredentialsProvider = () => IdentityCredentials | undefined
|
|
27
27
|
|
|
28
|
-
export
|
|
28
|
+
export const createTestIdentityCredentials = async (keyring: Keyring): Promise<IdentityCredentials> => {
|
|
29
29
|
const identityKey = await keyring.createKeyRecord({ type: KeyType.IDENTITY });
|
|
30
30
|
const deviceKey = await keyring.createKeyRecord({ type: KeyType.DEVICE });
|
|
31
31
|
const feedKey = await keyring.createKeyRecord({ type: KeyType.FEED });
|
|
@@ -53,9 +53,9 @@ export async function createTestIdentityCredentials (keyring: Keyring): Promise<
|
|
|
53
53
|
preferences: undefined,
|
|
54
54
|
contacts: undefined
|
|
55
55
|
};
|
|
56
|
-
}
|
|
56
|
+
};
|
|
57
57
|
|
|
58
|
-
export
|
|
58
|
+
export const deriveTestDeviceCredentials = async (identity: IdentityCredentials): Promise<IdentityCredentials> => {
|
|
59
59
|
const deviceKey = await identity.keyring.createKeyRecord({ type: KeyType.DEVICE });
|
|
60
60
|
const keyAdmit = createKeyAdmitMessage(identity.keyring, identity.identityKey.publicKey, deviceKey, [identity.identityKey]);
|
|
61
61
|
|
|
@@ -75,4 +75,4 @@ export async function deriveTestDeviceCredentials (identity: IdentityCredentials
|
|
|
75
75
|
deviceKeyChain
|
|
76
76
|
)
|
|
77
77
|
};
|
|
78
|
-
}
|
|
78
|
+
};
|
|
@@ -11,9 +11,7 @@ import { InvitationFactory, OfflineInvitationClaimer } from '../invitations';
|
|
|
11
11
|
* Creates network protocol plugin that allows peers to claim offline invitations.
|
|
12
12
|
* Plugin is intended to be used in data-party swarms.
|
|
13
13
|
*/
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
);
|
|
19
|
-
}
|
|
14
|
+
export const createOfflineInvitationPlugin = (invitationFactory: InvitationFactory, peerId: PublicKey) => new GreetingCommandPlugin(
|
|
15
|
+
peerId.asBuffer(),
|
|
16
|
+
OfflineInvitationClaimer.createOfflineInvitationClaimHandler(invitationFactory)
|
|
17
|
+
);
|
|
@@ -4,17 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
import debug from 'debug';
|
|
6
6
|
|
|
7
|
-
import { synchronized } from '@dxos/async';
|
|
8
7
|
import { discoveryKey, keyToString, PublicKey } from '@dxos/crypto';
|
|
9
|
-
import {
|
|
10
|
-
import type { HypercoreFeed } from '@dxos/feed-store';
|
|
8
|
+
import { PartyKey } from '@dxos/echo-protocol';
|
|
11
9
|
import { Protocol } from '@dxos/mesh-protocol';
|
|
12
10
|
import { MMSTTopology, NetworkManager, Plugin } from '@dxos/network-manager';
|
|
13
11
|
import { PresencePlugin } from '@dxos/protocol-plugin-presence';
|
|
14
|
-
import { Replicator } from '@dxos/protocol-plugin-replicator';
|
|
15
12
|
|
|
16
13
|
import { CredentialsProvider } from '.';
|
|
17
|
-
import { PartyFeedProvider } from '../pipeline/party-feed-provider';
|
|
18
14
|
|
|
19
15
|
const log = debug('dxos:echo-db:party-protocol-factory');
|
|
20
16
|
|
|
@@ -23,21 +19,15 @@ const log = debug('dxos:echo-db:party-protocol-factory');
|
|
|
23
19
|
*/
|
|
24
20
|
export class PartyProtocolFactory {
|
|
25
21
|
private readonly _presencePlugin = new PresencePlugin(this._peerId.asBuffer());
|
|
26
|
-
private readonly _replicatorProtocolPluginFactory: ReplicatorProtocolPluginFactory;
|
|
27
22
|
|
|
28
23
|
private _started = false;
|
|
29
24
|
|
|
30
25
|
constructor (
|
|
31
26
|
private readonly _partyKey: PartyKey,
|
|
32
27
|
private readonly _networkManager: NetworkManager,
|
|
33
|
-
private readonly _feedProvider: PartyFeedProvider,
|
|
34
28
|
private readonly _peerId: PublicKey,
|
|
35
29
|
private readonly _credentials: CredentialsProvider
|
|
36
|
-
) {
|
|
37
|
-
// Replication.
|
|
38
|
-
this._replicatorProtocolPluginFactory =
|
|
39
|
-
new ReplicatorProtocolPluginFactory(this._feedProvider);
|
|
40
|
-
}
|
|
30
|
+
) {}
|
|
41
31
|
|
|
42
32
|
async start (plugins: Plugin[]) {
|
|
43
33
|
if (this._started) {
|
|
@@ -73,9 +63,8 @@ export class PartyProtocolFactory {
|
|
|
73
63
|
await this._networkManager.leaveProtocolSwarm(this._partyKey);
|
|
74
64
|
}
|
|
75
65
|
|
|
76
|
-
private _createProtocol (channel: any, opts: {initiator: boolean}, extraPlugins: Plugin[]) {
|
|
66
|
+
private _createProtocol (channel: any, opts: { initiator: boolean }, extraPlugins: Plugin[]) {
|
|
77
67
|
const plugins: Plugin[] = [
|
|
78
|
-
...this._replicatorProtocolPluginFactory.createPlugins(),
|
|
79
68
|
...extraPlugins,
|
|
80
69
|
this._presencePlugin
|
|
81
70
|
];
|
|
@@ -118,45 +107,3 @@ export class PartyProtocolFactory {
|
|
|
118
107
|
return protocol;
|
|
119
108
|
}
|
|
120
109
|
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Creates the protocol plugin for feed replication.
|
|
124
|
-
*/
|
|
125
|
-
export class ReplicatorProtocolPluginFactory {
|
|
126
|
-
constructor (
|
|
127
|
-
private readonly _feedProvider: PartyFeedProvider
|
|
128
|
-
) {}
|
|
129
|
-
|
|
130
|
-
createPlugins () {
|
|
131
|
-
return [
|
|
132
|
-
new Replicator({
|
|
133
|
-
load: async () => {
|
|
134
|
-
const feeds = this._feedProvider.getFeeds();
|
|
135
|
-
log(`Loading feeds: ${feeds.map(feed => keyToString(feed.key))}`);
|
|
136
|
-
return feeds.map((feed) => ({ discoveryKey: feed.feed.discoveryKey }));
|
|
137
|
-
},
|
|
138
|
-
|
|
139
|
-
subscribe: (addFeedToReplicatedSet: (feed: any) => void) => {
|
|
140
|
-
return this._feedProvider.feedOpened.on(async (feed) => {
|
|
141
|
-
log(`Adding feed: ${feed.key.toHex()}`);
|
|
142
|
-
addFeedToReplicatedSet({ discoveryKey: feed.feed.discoveryKey });
|
|
143
|
-
});
|
|
144
|
-
},
|
|
145
|
-
|
|
146
|
-
replicate: async (remoteFeeds, info) => {
|
|
147
|
-
// We can ignore remoteFeeds entirely, since the set of feeds we want to replicate is dictated by the Party.
|
|
148
|
-
// TODO(telackey): Why are we opening feeds? Necessary or belt/braces thinking, or because open party does it?
|
|
149
|
-
const feeds = this._feedProvider.getFeeds();
|
|
150
|
-
log(`Replicating: peerId=${info.session}; feeds=${feeds.map(feed => feed.key.toHex())}`);
|
|
151
|
-
return feeds.map(feed => feed.feed);
|
|
152
|
-
}
|
|
153
|
-
})
|
|
154
|
-
];
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
@synchronized
|
|
158
|
-
private async _openFeed (key: FeedKey): Promise<HypercoreFeed> {
|
|
159
|
-
const descriptor = await this._feedProvider.createOrOpenReadOnlyFeed(key);
|
|
160
|
-
return descriptor.feed;
|
|
161
|
-
}
|
|
162
|
-
}
|