@dxos/client-services 0.6.13 → 0.6.14-main.69511f5
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/lib/browser/{chunk-CRXXOI45.mjs → chunk-PK5RMXEO.mjs} +6462 -5230
- package/dist/lib/browser/chunk-PK5RMXEO.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +7 -3
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +12 -8
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node/{chunk-PZ3JJJ3K.cjs → chunk-XDE6WELX.cjs} +6287 -5057
- package/dist/lib/node/chunk-XDE6WELX.cjs.map +7 -0
- package/dist/lib/node/index.cjs +50 -46
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +18 -13
- package/dist/lib/node/testing/index.cjs.map +3 -3
- package/dist/lib/node-esm/chunk-S5DTGWTU.mjs +8956 -0
- package/dist/lib/node-esm/chunk-S5DTGWTU.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +420 -0
- package/dist/lib/node-esm/index.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -0
- package/dist/lib/node-esm/testing/index.mjs +424 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/packlets/agents/edge-agent-manager.d.ts +35 -0
- package/dist/types/src/packlets/agents/edge-agent-manager.d.ts.map +1 -0
- package/dist/types/src/packlets/agents/edge-agent-service.d.ts +10 -0
- package/dist/types/src/packlets/agents/edge-agent-service.d.ts.map +1 -0
- package/dist/types/src/packlets/agents/index.d.ts +3 -0
- package/dist/types/src/packlets/agents/index.d.ts.map +1 -0
- package/dist/types/src/packlets/diagnostics/diagnostics-broadcast.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/authenticator.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/authenticator.node.test.d.ts +2 -0
- package/dist/types/src/packlets/identity/authenticator.node.test.d.ts.map +1 -0
- package/dist/types/src/packlets/identity/contacts-service.d.ts +1 -1
- package/dist/types/src/packlets/identity/contacts-service.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-manager.d.ts +28 -9
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts +18 -0
- package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts.map +1 -0
- package/dist/types/src/packlets/identity/identity-service.d.ts +7 -2
- package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity.d.ts +12 -3
- package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts +30 -0
- package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts.map +1 -0
- package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts +2 -1
- package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts +2 -1
- package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-state.d.ts +19 -0
- package/dist/types/src/packlets/invitations/invitation-state.d.ts.map +1 -0
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts +8 -8
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-context.d.ts +14 -9
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +2 -0
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts +7 -3
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space.d.ts +5 -3
- package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts +3 -0
- package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/edge-feed-replicator.test.d.ts +2 -0
- package/dist/types/src/packlets/spaces/edge-feed-replicator.test.d.ts.map +1 -0
- package/dist/types/src/packlets/spaces/epoch-migrations.d.ts +1 -1
- package/dist/types/src/packlets/spaces/epoch-migrations.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +35 -6
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/spaces-service.d.ts +1 -1
- package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
- package/dist/types/src/packlets/storage/storage.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts +1 -2
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
- package/dist/types/src/testing/setup.d.ts +3 -0
- package/dist/types/src/testing/setup.d.ts.map +1 -0
- package/dist/types/src/version.d.ts +1 -1
- package/dist/types/src/version.d.ts.map +1 -1
- package/package.json +44 -45
- package/src/index.ts +1 -0
- package/src/packlets/agents/edge-agent-manager.ts +163 -0
- package/src/packlets/agents/edge-agent-service.ts +42 -0
- package/src/packlets/agents/index.ts +6 -0
- package/src/packlets/devices/devices-service.test.ts +4 -5
- package/src/packlets/diagnostics/diagnostics-broadcast.ts +1 -0
- package/src/packlets/identity/{authenticator.test.ts → authenticator.node.test.ts} +2 -3
- package/src/packlets/identity/authenticator.ts +5 -2
- package/src/packlets/identity/contacts-service.ts +1 -1
- package/src/packlets/identity/identity-manager.test.ts +31 -16
- package/src/packlets/identity/identity-manager.ts +76 -32
- package/src/packlets/identity/identity-recovery-manager.ts +95 -0
- package/src/packlets/identity/identity-service.test.ts +5 -8
- package/src/packlets/identity/identity-service.ts +11 -5
- package/src/packlets/identity/identity.test.ts +130 -239
- package/src/packlets/identity/identity.ts +60 -17
- package/src/packlets/invitations/device-invitation-protocol.test.ts +7 -4
- package/src/packlets/invitations/device-invitation-protocol.ts +8 -2
- package/src/packlets/invitations/edge-invitation-handler.ts +185 -0
- package/src/packlets/invitations/invitation-guest-extenstion.ts +8 -4
- package/src/packlets/invitations/invitation-host-extension.ts +8 -7
- package/src/packlets/invitations/invitation-state.ts +112 -0
- package/src/packlets/invitations/invitations-handler.test.ts +16 -9
- package/src/packlets/invitations/invitations-handler.ts +57 -98
- package/src/packlets/invitations/space-invitation-protocol.test.ts +4 -3
- package/src/packlets/invitations/space-invitation-protocol.ts +5 -0
- package/src/packlets/logging/logging.test.ts +1 -2
- package/src/packlets/network/network-service.test.ts +2 -3
- package/src/packlets/services/service-context.test.ts +3 -1
- package/src/packlets/services/service-context.ts +113 -35
- package/src/packlets/services/service-host.test.ts +8 -12
- package/src/packlets/services/service-host.ts +25 -7
- package/src/packlets/services/service-registry.test.ts +1 -2
- package/src/packlets/spaces/data-space-manager.test.ts +2 -2
- package/src/packlets/spaces/data-space-manager.ts +44 -7
- package/src/packlets/spaces/data-space.ts +37 -6
- package/src/packlets/spaces/edge-feed-replicator.test.ts +252 -0
- package/src/packlets/spaces/edge-feed-replicator.ts +80 -22
- package/src/packlets/spaces/epoch-migrations.ts +2 -2
- package/src/packlets/spaces/notarization-plugin.test.ts +10 -7
- package/src/packlets/spaces/notarization-plugin.ts +196 -29
- package/src/packlets/spaces/spaces-service.test.ts +5 -9
- package/src/packlets/spaces/spaces-service.ts +6 -1
- package/src/packlets/storage/storage.ts +0 -1
- package/src/packlets/system/system-service.test.ts +1 -2
- package/src/packlets/testing/test-builder.ts +7 -4
- package/src/packlets/worker/worker-runtime.ts +2 -2
- package/src/testing/setup.ts +11 -0
- package/src/version.ts +1 -5
- package/dist/lib/browser/chunk-CRXXOI45.mjs.map +0 -7
- package/dist/lib/node/chunk-PZ3JJJ3K.cjs.map +0 -7
- package/dist/types/src/packlets/identity/authenticator.test.d.ts +0 -2
- package/dist/types/src/packlets/identity/authenticator.test.d.ts.map +0 -1
- package/dist/types/src/packlets/services/automerge-host.test.d.ts +0 -2
- package/dist/types/src/packlets/services/automerge-host.test.d.ts.map +0 -1
- package/src/packlets/services/automerge-host.test.ts +0 -60
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { type PushStream, scheduleTask, TimeoutError, type Trigger } from '@dxos/async';
|
|
6
6
|
import { INVITATION_TIMEOUT } from '@dxos/client-protocol';
|
|
7
7
|
import { type Context, ContextDisposedError } from '@dxos/context';
|
|
8
8
|
import { createKeyPair, sign } from '@dxos/crypto';
|
|
9
|
+
import { type EdgeHttpClient } from '@dxos/edge-client';
|
|
9
10
|
import { invariant } from '@dxos/invariant';
|
|
10
11
|
import { PublicKey } from '@dxos/keys';
|
|
11
12
|
import { log } from '@dxos/log';
|
|
@@ -19,17 +20,21 @@ import { type ExtensionContext, type TeleportExtension, type TeleportParams } fr
|
|
|
19
20
|
import { trace as _trace } from '@dxos/tracing';
|
|
20
21
|
import { ComplexSet } from '@dxos/util';
|
|
21
22
|
|
|
23
|
+
import { type EdgeInvitationConfig, EdgeInvitationHandler } from './edge-invitation-handler';
|
|
22
24
|
import { InvitationGuestExtension } from './invitation-guest-extenstion';
|
|
23
25
|
import { InvitationHostExtension, isAuthenticationRequired, MAX_OTP_ATTEMPTS } from './invitation-host-extension';
|
|
24
26
|
import { type InvitationProtocol } from './invitation-protocol';
|
|
27
|
+
import { createGuardedInvitationState } from './invitation-state';
|
|
25
28
|
import { InvitationTopology } from './invitation-topology';
|
|
26
|
-
import { stateToString } from './utils';
|
|
27
29
|
|
|
28
30
|
const metrics = _trace.metrics;
|
|
29
31
|
|
|
30
32
|
const MAX_DELEGATED_INVITATION_HOST_TRIES = 3;
|
|
31
33
|
|
|
32
|
-
type
|
|
34
|
+
export type InvitationConnectionParams = {
|
|
35
|
+
teleport: Partial<TeleportParams>;
|
|
36
|
+
edgeInvitations?: EdgeInvitationConfig;
|
|
37
|
+
};
|
|
33
38
|
|
|
34
39
|
/**
|
|
35
40
|
* Generic handler for Halo and Space invitations.
|
|
@@ -65,7 +70,8 @@ export class InvitationsHandler {
|
|
|
65
70
|
*/
|
|
66
71
|
constructor(
|
|
67
72
|
private readonly _networkManager: SwarmNetworkManager,
|
|
68
|
-
private readonly
|
|
73
|
+
private readonly _edgeClient?: EdgeHttpClient,
|
|
74
|
+
private readonly _connectionParams?: InvitationConnectionParams,
|
|
69
75
|
) {}
|
|
70
76
|
|
|
71
77
|
handleInvitationFlow(
|
|
@@ -74,8 +80,14 @@ export class InvitationsHandler {
|
|
|
74
80
|
protocol: InvitationProtocol,
|
|
75
81
|
invitation: Invitation,
|
|
76
82
|
): void {
|
|
83
|
+
log.verbose('dxos.sdk.invitations-handler.handleInvitationFlow', {
|
|
84
|
+
state: invitation.state,
|
|
85
|
+
invitationId: invitation.invitationId,
|
|
86
|
+
kind: invitation.kind,
|
|
87
|
+
type: invitation.type,
|
|
88
|
+
});
|
|
77
89
|
metrics.increment('dxos.invitation.created');
|
|
78
|
-
const guardedState =
|
|
90
|
+
const guardedState = createGuardedInvitationState(ctx, invitation, stream);
|
|
79
91
|
// Called for every connecting peer.
|
|
80
92
|
const createExtension = (): InvitationHostExtension => {
|
|
81
93
|
const extension = new InvitationHostExtension(guardedState.mutex, {
|
|
@@ -90,6 +102,10 @@ export class InvitationsHandler {
|
|
|
90
102
|
|
|
91
103
|
admit: async (admissionRequest) => {
|
|
92
104
|
try {
|
|
105
|
+
log.verbose('dxos.sdk.invitations-handler.host.admit', {
|
|
106
|
+
invitationId: invitation.invitationId,
|
|
107
|
+
...protocol.toJSON(),
|
|
108
|
+
});
|
|
93
109
|
const deviceKey = admissionRequest.device?.deviceKey ?? admissionRequest.space?.deviceKey;
|
|
94
110
|
invariant(deviceKey);
|
|
95
111
|
const admissionResponse = await protocol.admit(invitation, admissionRequest, extension.guestProfile);
|
|
@@ -201,6 +217,12 @@ export class InvitationsHandler {
|
|
|
201
217
|
otpEnteredTrigger: Trigger<string>,
|
|
202
218
|
deviceProfile?: DeviceProfileDocument,
|
|
203
219
|
): void {
|
|
220
|
+
log.verbose('dxos.sdk.invitations-handler.acceptInvitation', {
|
|
221
|
+
state: invitation.state,
|
|
222
|
+
invitationId: invitation.invitationId,
|
|
223
|
+
kind: invitation.kind,
|
|
224
|
+
type: invitation.type,
|
|
225
|
+
});
|
|
204
226
|
const { timeout = INVITATION_TIMEOUT } = invitation;
|
|
205
227
|
|
|
206
228
|
if (deviceProfile) {
|
|
@@ -208,7 +230,7 @@ export class InvitationsHandler {
|
|
|
208
230
|
}
|
|
209
231
|
|
|
210
232
|
const triedPeersIds = new ComplexSet(PublicKey.hash);
|
|
211
|
-
const guardedState =
|
|
233
|
+
const guardedState = createGuardedInvitationState(ctx, invitation, stream);
|
|
212
234
|
|
|
213
235
|
const shouldCancelInvitationFlow = (extension: InvitationGuestExtension) => {
|
|
214
236
|
const isLockedByAnotherConnection = guardedState.mutex.isLocked() && !extension.hasFlowLock();
|
|
@@ -263,16 +285,23 @@ export class InvitationsHandler {
|
|
|
263
285
|
timeout,
|
|
264
286
|
);
|
|
265
287
|
|
|
266
|
-
log('connected', { ...protocol.toJSON() });
|
|
288
|
+
log.verbose('dxos.sdk.invitations-handler.guest.connected', { ...protocol.toJSON() });
|
|
267
289
|
guardedState.set(extension, Invitation.State.CONNECTED);
|
|
268
290
|
|
|
269
291
|
// 1. Introduce guest to host.
|
|
270
|
-
log('introduce', {
|
|
292
|
+
log.verbose('dxos.sdk.invitations-handler.guest.introduce', {
|
|
293
|
+
invitationId: invitation.invitationId,
|
|
294
|
+
...protocol.toJSON(),
|
|
295
|
+
});
|
|
271
296
|
const introductionResponse = await extension.rpc.InvitationHostService.introduce({
|
|
272
297
|
invitationId: invitation.invitationId,
|
|
273
298
|
...protocol.createIntroduction(),
|
|
274
299
|
});
|
|
275
|
-
log('introduce
|
|
300
|
+
log.verbose('dxos.sdk.invitations-handler.guest.introduce-response', {
|
|
301
|
+
invitationId: invitation.invitationId,
|
|
302
|
+
...protocol.toJSON(),
|
|
303
|
+
authMethod: introductionResponse.authMethod,
|
|
304
|
+
});
|
|
276
305
|
invitation.authMethod = introductionResponse.authMethod;
|
|
277
306
|
|
|
278
307
|
// 2. Get authentication code.
|
|
@@ -298,7 +327,10 @@ export class InvitationsHandler {
|
|
|
298
327
|
}
|
|
299
328
|
|
|
300
329
|
// 3. Send admission credentials to host (with local space keys).
|
|
301
|
-
log('request
|
|
330
|
+
log.verbose('dxos.sdk.invitations-handler.guest.request-admission', {
|
|
331
|
+
invitationId: invitation.invitationId,
|
|
332
|
+
...protocol.toJSON(),
|
|
333
|
+
});
|
|
302
334
|
const admissionRequest = await protocol.createAdmissionRequest(deviceProfile);
|
|
303
335
|
const admissionResponse = await extension.rpc.InvitationHostService.admit(admissionRequest);
|
|
304
336
|
|
|
@@ -309,8 +341,11 @@ export class InvitationsHandler {
|
|
|
309
341
|
const result = await protocol.accept(admissionResponse, admissionRequest);
|
|
310
342
|
|
|
311
343
|
// 5. Success.
|
|
312
|
-
log('admitted
|
|
313
|
-
|
|
344
|
+
log.verbose('dxos.sdk.invitations-handler.guest.admitted-by-host', {
|
|
345
|
+
invitationId: invitation.invitationId,
|
|
346
|
+
...protocol.toJSON(),
|
|
347
|
+
});
|
|
348
|
+
guardedState.complete({
|
|
314
349
|
...guardedState.current,
|
|
315
350
|
...result,
|
|
316
351
|
state: Invitation.State.SUCCESS,
|
|
@@ -346,6 +381,15 @@ export class InvitationsHandler {
|
|
|
346
381
|
return extension;
|
|
347
382
|
};
|
|
348
383
|
|
|
384
|
+
const edgeInvitationHandler = new EdgeInvitationHandler(this._connectionParams?.edgeInvitations, this._edgeClient, {
|
|
385
|
+
onInvitationSuccess: async (admissionResponse, admissionRequest) => {
|
|
386
|
+
const result = await protocol.accept(admissionResponse, admissionRequest);
|
|
387
|
+
log.info('admitted by edge', { ...protocol.toJSON() });
|
|
388
|
+
guardedState.complete({ ...guardedState.current, ...result, state: Invitation.State.SUCCESS });
|
|
389
|
+
},
|
|
390
|
+
});
|
|
391
|
+
edgeInvitationHandler.handle(ctx, guardedState, protocol, deviceProfile);
|
|
392
|
+
|
|
349
393
|
scheduleTask(ctx, async () => {
|
|
350
394
|
const error = protocol.checkInvitation(invitation);
|
|
351
395
|
if (error) {
|
|
@@ -389,7 +433,7 @@ export class InvitationsHandler {
|
|
|
389
433
|
topic: invitation.swarmKey,
|
|
390
434
|
protocolProvider: createTeleportProtocolFactory(async (teleport) => {
|
|
391
435
|
teleport.addExtension('dxos.halo.invitations', extensionFactory());
|
|
392
|
-
}, this.
|
|
436
|
+
}, this._connectionParams?.teleport),
|
|
393
437
|
topology: new InvitationTopology(role),
|
|
394
438
|
label,
|
|
395
439
|
});
|
|
@@ -397,90 +441,6 @@ export class InvitationsHandler {
|
|
|
397
441
|
return swarmConnection;
|
|
398
442
|
}
|
|
399
443
|
|
|
400
|
-
/**
|
|
401
|
-
* A utility object for serializing invitation state changes by multiple concurrent
|
|
402
|
-
* invitation flow connections.
|
|
403
|
-
*/
|
|
404
|
-
private _createGuardedState(ctx: Context, invitation: Invitation, stream: PushStream<Invitation>) {
|
|
405
|
-
// the mutex guards invitation flow on host and guest side, making sure only one flow is currently active
|
|
406
|
-
// deadlocks seem very unlikely because hosts don't initiate multiple connections
|
|
407
|
-
// even if this somehow happens that there are 2 guests (A, B) and 2 hosts (1, 2) and:
|
|
408
|
-
// A has lock for flow with 1, B has lock for flow with 2
|
|
409
|
-
// 1 has lock for flow with B, 2 has lock for flow with A
|
|
410
|
-
// there'll be a 10-second introduction timeout after which connection will be closed and deadlock broken
|
|
411
|
-
const mutex = new Mutex();
|
|
412
|
-
let lastActiveExtension: any = null;
|
|
413
|
-
let currentInvitation = { ...invitation };
|
|
414
|
-
const isStateChangeAllowed = (extension: InvitationExtension | null) => {
|
|
415
|
-
if (ctx.disposed || (extension !== null && mutex.isLocked() && !extension.hasFlowLock())) {
|
|
416
|
-
return false;
|
|
417
|
-
}
|
|
418
|
-
// don't allow transitions from a terminal state unless a new extension acquired mutex
|
|
419
|
-
// handles a case when error occurs (e.g. connection is closed) after we completed the flow
|
|
420
|
-
// successfully or already reported another error
|
|
421
|
-
return extension == null || lastActiveExtension !== extension || this._isNotTerminal(currentInvitation.state);
|
|
422
|
-
};
|
|
423
|
-
return {
|
|
424
|
-
mutex,
|
|
425
|
-
get current() {
|
|
426
|
-
return currentInvitation;
|
|
427
|
-
},
|
|
428
|
-
// disposing context prevents any further state updates
|
|
429
|
-
complete: (newState: Partial<Invitation>) => {
|
|
430
|
-
currentInvitation = { ...currentInvitation, ...newState };
|
|
431
|
-
stream.next(currentInvitation);
|
|
432
|
-
return ctx.dispose();
|
|
433
|
-
},
|
|
434
|
-
set: (extension: InvitationExtension | null, newState: Invitation.State): boolean => {
|
|
435
|
-
if (isStateChangeAllowed(extension)) {
|
|
436
|
-
this._logStateUpdate(currentInvitation, extension, newState);
|
|
437
|
-
currentInvitation = { ...currentInvitation, state: newState };
|
|
438
|
-
stream.next(currentInvitation);
|
|
439
|
-
lastActiveExtension = extension;
|
|
440
|
-
return true;
|
|
441
|
-
}
|
|
442
|
-
return false;
|
|
443
|
-
},
|
|
444
|
-
error: (extension: InvitationExtension | null, error: any): boolean => {
|
|
445
|
-
if (isStateChangeAllowed(extension)) {
|
|
446
|
-
this._logStateUpdate(currentInvitation, extension, Invitation.State.ERROR);
|
|
447
|
-
currentInvitation = { ...currentInvitation, state: Invitation.State.ERROR };
|
|
448
|
-
stream.next(currentInvitation);
|
|
449
|
-
stream.error(error);
|
|
450
|
-
lastActiveExtension = extension;
|
|
451
|
-
return true;
|
|
452
|
-
}
|
|
453
|
-
return false;
|
|
454
|
-
},
|
|
455
|
-
};
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
private _logStateUpdate(invitation: Invitation, actor: any, newState: Invitation.State) {
|
|
459
|
-
if (this._isNotTerminal(newState)) {
|
|
460
|
-
log('invitation state update', {
|
|
461
|
-
actor: actor?.constructor.name,
|
|
462
|
-
newState: stateToString(newState),
|
|
463
|
-
oldState: stateToString(invitation.state),
|
|
464
|
-
});
|
|
465
|
-
} else {
|
|
466
|
-
log.info('invitation state update', {
|
|
467
|
-
actor: actor?.constructor.name,
|
|
468
|
-
newState: stateToString(newState),
|
|
469
|
-
oldState: stateToString(invitation.state),
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
private _isNotTerminal(currentState: Invitation.State): boolean {
|
|
475
|
-
return ![
|
|
476
|
-
Invitation.State.SUCCESS,
|
|
477
|
-
Invitation.State.ERROR,
|
|
478
|
-
Invitation.State.CANCELLED,
|
|
479
|
-
Invitation.State.TIMEOUT,
|
|
480
|
-
Invitation.State.EXPIRED,
|
|
481
|
-
].includes(currentState);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
444
|
private async _handleGuestOtpAuth(
|
|
485
445
|
extension: InvitationGuestExtension,
|
|
486
446
|
setState: (newState: Invitation.State) => void,
|
|
@@ -523,7 +483,6 @@ export class InvitationsHandler {
|
|
|
523
483
|
throw new Error('challenge missing in the introduction');
|
|
524
484
|
}
|
|
525
485
|
log('sending authentication request');
|
|
526
|
-
setState(Invitation.State.AUTHENTICATING);
|
|
527
486
|
const signature = sign(Buffer.from(introductionResponse.challenge), invitation.guestKeypair.privateKey);
|
|
528
487
|
const response = await extension.rpc.InvitationHostService.authenticate({
|
|
529
488
|
signedChallenge: signature,
|
|
@@ -2,20 +2,21 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { expect } from '
|
|
5
|
+
import { onTestFinished, describe, expect, test } from 'vitest';
|
|
6
6
|
|
|
7
7
|
import { asyncChain, Trigger } from '@dxos/async';
|
|
8
8
|
import { raise } from '@dxos/debug';
|
|
9
9
|
import { AlreadyJoinedError } from '@dxos/protocols';
|
|
10
10
|
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
11
|
-
import { afterTest, describe, test } from '@dxos/test';
|
|
12
11
|
|
|
13
12
|
import { type ServiceContext } from '../services';
|
|
14
13
|
import { createIdentity, createPeers } from '../testing';
|
|
15
14
|
import { acceptInvitation, createInvitation, performInvitation } from '../testing/invitation-utils';
|
|
16
15
|
|
|
17
16
|
const closeAfterTest = async (peer: ServiceContext) => {
|
|
18
|
-
|
|
17
|
+
onTestFinished(async () => {
|
|
18
|
+
await peer.close();
|
|
19
|
+
});
|
|
19
20
|
return peer;
|
|
20
21
|
};
|
|
21
22
|
|
|
@@ -40,6 +40,7 @@ export class SpaceInvitationProtocol implements InvitationProtocol {
|
|
|
40
40
|
|
|
41
41
|
toJSON(): object {
|
|
42
42
|
return {
|
|
43
|
+
kind: 'space',
|
|
43
44
|
deviceKey: this._signingContext.deviceKey,
|
|
44
45
|
spaceKey: this._spaceKey,
|
|
45
46
|
};
|
|
@@ -60,9 +61,13 @@ export class SpaceInvitationProtocol implements InvitationProtocol {
|
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
getInvitationContext(): Partial<Invitation> & Pick<Invitation, 'kind'> {
|
|
64
|
+
invariant(this._spaceKey);
|
|
65
|
+
const space = this._spaceManager.spaces.get(this._spaceKey);
|
|
66
|
+
invariant(space);
|
|
63
67
|
return {
|
|
64
68
|
kind: Invitation.Kind.SPACE,
|
|
65
69
|
spaceKey: this._spaceKey,
|
|
70
|
+
spaceId: space.id,
|
|
66
71
|
};
|
|
67
72
|
}
|
|
68
73
|
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { expect } from '
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
|
|
6
6
|
|
|
7
7
|
import { Trigger } from '@dxos/async';
|
|
8
8
|
import { log, LogLevel } from '@dxos/log';
|
|
9
9
|
import { type LogEntry } from '@dxos/protocols/proto/dxos/client/services';
|
|
10
|
-
import { beforeEach, describe, test } from '@dxos/test';
|
|
11
10
|
|
|
12
11
|
import { LoggingServiceImpl } from './logging-service';
|
|
13
12
|
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { expect } from '
|
|
5
|
+
import { afterEach, onTestFinished, beforeEach, describe, expect, test } from 'vitest';
|
|
6
6
|
|
|
7
7
|
import { Trigger } from '@dxos/async';
|
|
8
8
|
import { Context } from '@dxos/context';
|
|
9
9
|
import { type NetworkService, ConnectionState } from '@dxos/protocols/proto/dxos/client/services';
|
|
10
|
-
import { afterEach, afterTest, beforeEach, describe, test } from '@dxos/test';
|
|
11
10
|
|
|
12
11
|
import { NetworkServiceImpl } from './network-service';
|
|
13
12
|
import { type ServiceContext } from '../services';
|
|
@@ -41,7 +40,7 @@ describe('NetworkService', () => {
|
|
|
41
40
|
query.subscribe(({ swarm }) => {
|
|
42
41
|
result.wake(swarm);
|
|
43
42
|
});
|
|
44
|
-
|
|
43
|
+
onTestFinished(() => query.close());
|
|
45
44
|
expect(await result.wait()).to.equal(ConnectionState.ONLINE);
|
|
46
45
|
|
|
47
46
|
result = new Trigger<ConnectionState | undefined>();
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { describe, test } from 'vitest';
|
|
6
|
+
|
|
5
7
|
import { MemorySignalManagerContext, MemorySignalManager } from '@dxos/messaging';
|
|
6
8
|
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
7
|
-
import {
|
|
9
|
+
import { openAndClose } from '@dxos/test-utils';
|
|
8
10
|
|
|
9
11
|
import { createServiceContext, performInvitation } from '../testing';
|
|
10
12
|
|
|
@@ -2,13 +2,20 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Trigger } from '@dxos/async';
|
|
5
|
+
import { Mutex, Trigger } from '@dxos/async';
|
|
6
6
|
import { Context, Resource } from '@dxos/context';
|
|
7
7
|
import { getCredentialAssertion, type CredentialProcessor } from '@dxos/credentials';
|
|
8
|
-
import { failUndefined } from '@dxos/debug';
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
import { failUndefined, warnAfterTimeout } from '@dxos/debug';
|
|
9
|
+
import {
|
|
10
|
+
EchoEdgeReplicator,
|
|
11
|
+
EchoHost,
|
|
12
|
+
MeshEchoReplicator,
|
|
13
|
+
MetadataStore,
|
|
14
|
+
SpaceManager,
|
|
15
|
+
valueEncoding,
|
|
16
|
+
} from '@dxos/echo-pipeline';
|
|
17
|
+
import { createChainEdgeIdentity, createEphemeralEdgeIdentity } from '@dxos/edge-client';
|
|
18
|
+
import type { EdgeHttpClient, EdgeConnection, EdgeIdentity } from '@dxos/edge-client';
|
|
12
19
|
import { FeedFactory, FeedStore } from '@dxos/feed-store';
|
|
13
20
|
import { invariant } from '@dxos/invariant';
|
|
14
21
|
import { Keyring } from '@dxos/keyring';
|
|
@@ -23,19 +30,21 @@ import { type Runtime } from '@dxos/protocols/proto/dxos/config';
|
|
|
23
30
|
import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
24
31
|
import { type Credential, type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
25
32
|
import { type Storage } from '@dxos/random-access-storage';
|
|
26
|
-
import type { TeleportParams } from '@dxos/teleport';
|
|
27
33
|
import { BlobStore } from '@dxos/teleport-extension-object-sync';
|
|
28
34
|
import { trace as Trace } from '@dxos/tracing';
|
|
29
35
|
import { safeInstanceof } from '@dxos/util';
|
|
30
36
|
|
|
37
|
+
import { EdgeAgentManager } from '../agents';
|
|
31
38
|
import {
|
|
32
39
|
IdentityManager,
|
|
33
40
|
type CreateIdentityOptions,
|
|
34
|
-
type
|
|
41
|
+
type IdentityManagerParams,
|
|
35
42
|
type JoinIdentityParams,
|
|
36
43
|
} from '../identity';
|
|
44
|
+
import { EdgeIdentityRecoveryManager } from '../identity/identity-recovery-manager';
|
|
37
45
|
import {
|
|
38
46
|
DeviceInvitationProtocol,
|
|
47
|
+
type InvitationConnectionParams,
|
|
39
48
|
InvitationsHandler,
|
|
40
49
|
InvitationsManager,
|
|
41
50
|
SpaceInvitationProtocol,
|
|
@@ -43,9 +52,12 @@ import {
|
|
|
43
52
|
} from '../invitations';
|
|
44
53
|
import { DataSpaceManager, type DataSpaceManagerRuntimeParams, type SigningContext } from '../spaces';
|
|
45
54
|
|
|
46
|
-
export type ServiceContextRuntimeParams =
|
|
55
|
+
export type ServiceContextRuntimeParams = Pick<
|
|
56
|
+
IdentityManagerParams,
|
|
57
|
+
'devicePresenceOfflineTimeout' | 'devicePresenceAnnounceInterval'
|
|
58
|
+
> &
|
|
47
59
|
DataSpaceManagerRuntimeParams & {
|
|
48
|
-
invitationConnectionDefaultParams?:
|
|
60
|
+
invitationConnectionDefaultParams?: InvitationConnectionParams;
|
|
49
61
|
disableP2pReplication?: boolean;
|
|
50
62
|
};
|
|
51
63
|
/**
|
|
@@ -56,6 +68,8 @@ export type ServiceContextRuntimeParams = IdentityManagerRuntimeParams &
|
|
|
56
68
|
@safeInstanceof('dxos.client-services.ServiceContext')
|
|
57
69
|
@Trace.resource()
|
|
58
70
|
export class ServiceContext extends Resource {
|
|
71
|
+
private readonly _edgeIdentityUpdateMutex = new Mutex();
|
|
72
|
+
|
|
59
73
|
public readonly initialized = new Trigger();
|
|
60
74
|
public readonly metadataStore: MetadataStore;
|
|
61
75
|
public readonly blobStore: BlobStore;
|
|
@@ -63,6 +77,7 @@ export class ServiceContext extends Resource {
|
|
|
63
77
|
public readonly keyring: Keyring;
|
|
64
78
|
public readonly spaceManager: SpaceManager;
|
|
65
79
|
public readonly identityManager: IdentityManager;
|
|
80
|
+
public readonly recoveryManager: EdgeIdentityRecoveryManager;
|
|
66
81
|
public readonly invitations: InvitationsHandler;
|
|
67
82
|
public readonly invitationsManager: InvitationsManager;
|
|
68
83
|
public readonly echoHost: EchoHost;
|
|
@@ -71,6 +86,7 @@ export class ServiceContext extends Resource {
|
|
|
71
86
|
|
|
72
87
|
// Initialized after identity is initialized.
|
|
73
88
|
public dataSpaceManager?: DataSpaceManager;
|
|
89
|
+
public edgeAgentManager?: EdgeAgentManager;
|
|
74
90
|
|
|
75
91
|
private readonly _handlerFactories = new Map<
|
|
76
92
|
Invitation.Kind,
|
|
@@ -87,6 +103,7 @@ export class ServiceContext extends Resource {
|
|
|
87
103
|
public readonly networkManager: SwarmNetworkManager,
|
|
88
104
|
public readonly signalManager: SignalManager,
|
|
89
105
|
private readonly _edgeConnection: EdgeConnection | undefined,
|
|
106
|
+
private readonly _edgeHttpClient: EdgeHttpClient | undefined,
|
|
90
107
|
public readonly _runtimeParams?: ServiceContextRuntimeParams,
|
|
91
108
|
private readonly _edgeFeatures?: Runtime.Client.EdgeFeatures,
|
|
92
109
|
) {
|
|
@@ -116,31 +133,22 @@ export class ServiceContext extends Resource {
|
|
|
116
133
|
disableP2pReplication: this._runtimeParams?.disableP2pReplication,
|
|
117
134
|
});
|
|
118
135
|
|
|
119
|
-
this.identityManager = new IdentityManager(
|
|
120
|
-
this.metadataStore,
|
|
136
|
+
this.identityManager = new IdentityManager({
|
|
137
|
+
metadataStore: this.metadataStore,
|
|
138
|
+
keyring: this.keyring,
|
|
139
|
+
feedStore: this.feedStore,
|
|
140
|
+
spaceManager: this.spaceManager,
|
|
141
|
+
devicePresenceOfflineTimeout: this._runtimeParams?.devicePresenceOfflineTimeout,
|
|
142
|
+
devicePresenceAnnounceInterval: this._runtimeParams?.devicePresenceAnnounceInterval,
|
|
143
|
+
edgeConnection: this._edgeConnection,
|
|
144
|
+
edgeFeatures: this._edgeFeatures,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
this.recoveryManager = new EdgeIdentityRecoveryManager(
|
|
121
148
|
this.keyring,
|
|
122
|
-
this.
|
|
123
|
-
this.
|
|
124
|
-
this.
|
|
125
|
-
{
|
|
126
|
-
onIdentityConstruction: (identity) => {
|
|
127
|
-
if (this._edgeConnection) {
|
|
128
|
-
log.info('Setting identity on edge connection', {
|
|
129
|
-
identity: identity.identityKey.toHex(),
|
|
130
|
-
oldIdentity: this._edgeConnection.identityKey,
|
|
131
|
-
swarms: this.networkManager.topics,
|
|
132
|
-
});
|
|
133
|
-
this._edgeConnection.setIdentity({
|
|
134
|
-
peerKey: identity.deviceKey.toHex(),
|
|
135
|
-
identityKey: identity.identityKey.toHex(),
|
|
136
|
-
});
|
|
137
|
-
this.networkManager.setPeerInfo({
|
|
138
|
-
identityKey: identity.identityKey.toHex(),
|
|
139
|
-
peerKey: identity.deviceKey.toHex(),
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
},
|
|
143
|
-
},
|
|
149
|
+
this._edgeHttpClient,
|
|
150
|
+
() => this.identityManager.identity,
|
|
151
|
+
this._acceptIdentity.bind(this),
|
|
144
152
|
);
|
|
145
153
|
|
|
146
154
|
this.echoHost = new EchoHost({ kv: this.level });
|
|
@@ -149,6 +157,7 @@ export class ServiceContext extends Resource {
|
|
|
149
157
|
|
|
150
158
|
this.invitations = new InvitationsHandler(
|
|
151
159
|
this.networkManager, //
|
|
160
|
+
this._edgeHttpClient,
|
|
152
161
|
_runtimeParams?.invitationConnectionDefaultParams,
|
|
153
162
|
);
|
|
154
163
|
this.invitationsManager = new InvitationsManager(
|
|
@@ -185,6 +194,11 @@ export class ServiceContext extends Resource {
|
|
|
185
194
|
|
|
186
195
|
log('opening...');
|
|
187
196
|
log.trace('dxos.sdk.service-context.open', trace.begin({ id: this._instanceId }));
|
|
197
|
+
|
|
198
|
+
await this.identityManager.open(ctx);
|
|
199
|
+
|
|
200
|
+
await this._setNetworkIdentity();
|
|
201
|
+
|
|
188
202
|
await this._edgeConnection?.open();
|
|
189
203
|
await this.signalManager.open();
|
|
190
204
|
await this.networkManager.open();
|
|
@@ -200,9 +214,9 @@ export class ServiceContext extends Resource {
|
|
|
200
214
|
|
|
201
215
|
await this.metadataStore.load();
|
|
202
216
|
await this.spaceManager.open();
|
|
203
|
-
await this.identityManager.open(ctx);
|
|
204
217
|
|
|
205
218
|
if (this.identityManager.identity) {
|
|
219
|
+
await this.identityManager.identity.joinNetwork();
|
|
206
220
|
await this._initialize(ctx);
|
|
207
221
|
}
|
|
208
222
|
|
|
@@ -219,6 +233,7 @@ export class ServiceContext extends Resource {
|
|
|
219
233
|
await this.identityManager.identity.space.spaceState.removeCredentialProcessor(this._deviceSpaceSync);
|
|
220
234
|
}
|
|
221
235
|
await this.dataSpaceManager?.close();
|
|
236
|
+
await this.edgeAgentManager?.close();
|
|
222
237
|
await this.identityManager.close();
|
|
223
238
|
await this.spaceManager.close();
|
|
224
239
|
await this.feedStore.close();
|
|
@@ -234,11 +249,16 @@ export class ServiceContext extends Resource {
|
|
|
234
249
|
|
|
235
250
|
async createIdentity(params: CreateIdentityOptions = {}) {
|
|
236
251
|
const identity = await this.identityManager.createIdentity(params);
|
|
252
|
+
await this._setNetworkIdentity();
|
|
253
|
+
await identity.joinNetwork();
|
|
237
254
|
await this._initialize(new Context());
|
|
238
255
|
return identity;
|
|
239
256
|
}
|
|
240
257
|
|
|
241
258
|
getInvitationHandler(invitation: Partial<Invitation> & Pick<Invitation, 'kind'>): InvitationProtocol {
|
|
259
|
+
if (this.identityManager.identity == null && invitation.kind === Invitation.Kind.SPACE) {
|
|
260
|
+
throw new Error('Identity must be created before joining a space.');
|
|
261
|
+
}
|
|
242
262
|
const factory = this._handlerFactories.get(invitation.kind);
|
|
243
263
|
invariant(factory, `Unknown invitation kind: ${invitation.kind}`);
|
|
244
264
|
return factory(invitation);
|
|
@@ -255,7 +275,10 @@ export class ServiceContext extends Resource {
|
|
|
255
275
|
}
|
|
256
276
|
|
|
257
277
|
private async _acceptIdentity(params: JoinIdentityParams) {
|
|
258
|
-
const identity = await this.identityManager.
|
|
278
|
+
const { identity, identityRecord } = await this.identityManager.prepareIdentity(params);
|
|
279
|
+
await this._setNetworkIdentity({ deviceCredential: params.authorizedDeviceCredential! });
|
|
280
|
+
await identity.joinNetwork();
|
|
281
|
+
await this.identityManager.acceptIdentity(identity, identityRecord, params.deviceProfile);
|
|
259
282
|
await this._initialize(new Context());
|
|
260
283
|
return identity;
|
|
261
284
|
}
|
|
@@ -292,6 +315,7 @@ export class ServiceContext extends Resource {
|
|
|
292
315
|
echoHost: this.echoHost,
|
|
293
316
|
invitationsManager: this.invitationsManager,
|
|
294
317
|
edgeConnection: this._edgeConnection,
|
|
318
|
+
edgeHttpClient: this._edgeHttpClient,
|
|
295
319
|
echoEdgeReplicator: this._echoEdgeReplicator,
|
|
296
320
|
meshReplicator: this._meshReplicator,
|
|
297
321
|
runtimeParams: this._runtimeParams as DataSpaceManagerRuntimeParams,
|
|
@@ -299,6 +323,14 @@ export class ServiceContext extends Resource {
|
|
|
299
323
|
});
|
|
300
324
|
await this.dataSpaceManager.open();
|
|
301
325
|
|
|
326
|
+
this.edgeAgentManager = new EdgeAgentManager(
|
|
327
|
+
this._edgeFeatures,
|
|
328
|
+
this._edgeHttpClient,
|
|
329
|
+
this.dataSpaceManager,
|
|
330
|
+
identity,
|
|
331
|
+
);
|
|
332
|
+
await this.edgeAgentManager.open();
|
|
333
|
+
|
|
302
334
|
this._handlerFactories.set(Invitation.Kind.SPACE, (invitation) => {
|
|
303
335
|
invariant(this.dataSpaceManager, 'dataSpaceManager not initialized yet');
|
|
304
336
|
return new SpaceInvitationProtocol(this.dataSpaceManager, signingContext, this.keyring, invitation.spaceKey);
|
|
@@ -338,4 +370,50 @@ export class ServiceContext extends Resource {
|
|
|
338
370
|
|
|
339
371
|
await identity.space.spaceState.addCredentialProcessor(this._deviceSpaceSync);
|
|
340
372
|
}
|
|
373
|
+
|
|
374
|
+
private async _setNetworkIdentity(params?: { deviceCredential: Credential }) {
|
|
375
|
+
using _ = await this._edgeIdentityUpdateMutex.acquire();
|
|
376
|
+
|
|
377
|
+
let edgeIdentity: EdgeIdentity;
|
|
378
|
+
const identity = this.identityManager.identity;
|
|
379
|
+
if (identity) {
|
|
380
|
+
log.info('Setting identity on edge connection', {
|
|
381
|
+
identity: identity.identityKey.toHex(),
|
|
382
|
+
swarms: this.networkManager.topics,
|
|
383
|
+
});
|
|
384
|
+
if (params?.deviceCredential) {
|
|
385
|
+
edgeIdentity = await createChainEdgeIdentity(
|
|
386
|
+
identity.signer,
|
|
387
|
+
identity.identityKey,
|
|
388
|
+
identity.deviceKey,
|
|
389
|
+
{ credential: params.deviceCredential },
|
|
390
|
+
[], // TODO(dmaretskyi): Service access credentials.
|
|
391
|
+
);
|
|
392
|
+
} else {
|
|
393
|
+
// TODO: throw here or from identity if device chain can't be loaded, to avoid indefinite hangup
|
|
394
|
+
await warnAfterTimeout(10_000, 'Waiting for identity to be ready for edge connection', async () => {
|
|
395
|
+
await identity.ready();
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
invariant(identity.deviceCredentialChain);
|
|
399
|
+
|
|
400
|
+
edgeIdentity = await createChainEdgeIdentity(
|
|
401
|
+
identity.signer,
|
|
402
|
+
identity.identityKey,
|
|
403
|
+
identity.deviceKey,
|
|
404
|
+
identity.deviceCredentialChain,
|
|
405
|
+
[], // TODO(dmaretskyi): Service access credentials.
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
} else {
|
|
409
|
+
edgeIdentity = await createEphemeralEdgeIdentity();
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
this._edgeConnection?.setIdentity(edgeIdentity);
|
|
413
|
+
this._edgeHttpClient?.setIdentity(edgeIdentity);
|
|
414
|
+
this.networkManager.setPeerInfo({
|
|
415
|
+
identityKey: edgeIdentity.identityKey,
|
|
416
|
+
peerKey: edgeIdentity.peerKey,
|
|
417
|
+
});
|
|
418
|
+
}
|
|
341
419
|
}
|