@waku/core 0.0.36 → 0.0.37-2ed5ddc.0

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.
Files changed (45) hide show
  1. package/bundle/index.js +4628 -873
  2. package/bundle/lib/message/version_0.js +1 -1
  3. package/bundle/{version_0-9DPFjcJG.js → version_0-Bc0h7ah2.js} +312 -33
  4. package/dist/.tsbuildinfo +1 -1
  5. package/dist/lib/connection_manager/connection_limiter.d.ts +35 -0
  6. package/dist/lib/connection_manager/connection_limiter.js +103 -0
  7. package/dist/lib/connection_manager/connection_limiter.js.map +1 -0
  8. package/dist/lib/connection_manager/connection_manager.d.ts +19 -105
  9. package/dist/lib/connection_manager/connection_manager.js +64 -507
  10. package/dist/lib/connection_manager/connection_manager.js.map +1 -1
  11. package/dist/lib/connection_manager/discovery_dialer.d.ts +32 -0
  12. package/dist/lib/connection_manager/discovery_dialer.js +131 -0
  13. package/dist/lib/connection_manager/discovery_dialer.js.map +1 -0
  14. package/dist/lib/connection_manager/keep_alive_manager.d.ts +17 -7
  15. package/dist/lib/connection_manager/keep_alive_manager.js +110 -74
  16. package/dist/lib/connection_manager/keep_alive_manager.js.map +1 -1
  17. package/dist/lib/connection_manager/network_monitor.d.ts +22 -0
  18. package/dist/lib/connection_manager/network_monitor.js +63 -0
  19. package/dist/lib/connection_manager/network_monitor.js.map +1 -0
  20. package/dist/lib/connection_manager/shard_reader.d.ts +28 -0
  21. package/dist/lib/connection_manager/shard_reader.js +70 -0
  22. package/dist/lib/connection_manager/shard_reader.js.map +1 -0
  23. package/dist/lib/connection_manager/utils.d.ts +16 -1
  24. package/dist/lib/connection_manager/utils.js +23 -0
  25. package/dist/lib/connection_manager/utils.js.map +1 -1
  26. package/dist/lib/filter/filter.d.ts +2 -3
  27. package/dist/lib/filter/filter.js +8 -25
  28. package/dist/lib/filter/filter.js.map +1 -1
  29. package/dist/lib/light_push/light_push.d.ts +2 -3
  30. package/dist/lib/light_push/light_push.js +1 -3
  31. package/dist/lib/light_push/light_push.js.map +1 -1
  32. package/dist/lib/store/store.d.ts +2 -3
  33. package/dist/lib/store/store.js +1 -3
  34. package/dist/lib/store/store.js.map +1 -1
  35. package/package.json +1 -125
  36. package/src/lib/connection_manager/connection_limiter.ts +161 -0
  37. package/src/lib/connection_manager/connection_manager.ts +85 -674
  38. package/src/lib/connection_manager/discovery_dialer.ts +195 -0
  39. package/src/lib/connection_manager/keep_alive_manager.ts +154 -87
  40. package/src/lib/connection_manager/network_monitor.ts +88 -0
  41. package/src/lib/connection_manager/shard_reader.ts +134 -0
  42. package/src/lib/connection_manager/utils.ts +27 -1
  43. package/src/lib/filter/filter.ts +7 -28
  44. package/src/lib/light_push/light_push.ts +1 -5
  45. package/src/lib/store/store.ts +1 -5
@@ -1,166 +1,56 @@
1
- import {
2
- type Connection,
3
- isPeerId,
4
- type Peer,
5
- type PeerId,
6
- type PeerInfo,
7
- type Stream,
8
- TypedEventEmitter
9
- } from "@libp2p/interface";
10
- import { Multiaddr, multiaddr, MultiaddrInput } from "@multiformats/multiaddr";
1
+ import { type Peer, type PeerId, type Stream } from "@libp2p/interface";
2
+ import { MultiaddrInput } from "@multiformats/multiaddr";
11
3
  import {
12
4
  ConnectionManagerOptions,
13
- DiscoveryTrigger,
14
- DNS_DISCOVERY_TAG,
15
- EConnectionStateEvents,
16
- EPeersByDiscoveryEvents,
17
5
  IConnectionManager,
18
- IConnectionStateEvents,
19
- IPeersByDiscoveryEvents,
20
6
  IRelay,
21
- PeersByDiscoveryResult,
22
- PubsubTopic,
23
- ShardInfo
7
+ IWakuEventEmitter,
8
+ NetworkConfig,
9
+ PubsubTopic
24
10
  } from "@waku/interfaces";
25
- import { Libp2p, Tags } from "@waku/interfaces";
26
- import { decodeRelayShard, shardInfoToPubsubTopics } from "@waku/utils";
11
+ import { Libp2p } from "@waku/interfaces";
27
12
  import { Logger } from "@waku/utils";
28
13
 
14
+ import { ConnectionLimiter } from "./connection_limiter.js";
15
+ import { DiscoveryDialer } from "./discovery_dialer.js";
29
16
  import { KeepAliveManager } from "./keep_alive_manager.js";
30
- import { getPeerPing } from "./utils.js";
17
+ import { NetworkMonitor } from "./network_monitor.js";
18
+ import { ShardReader } from "./shard_reader.js";
19
+ import { getPeerPing, mapToPeerId, mapToPeerIdOrMultiaddr } from "./utils.js";
31
20
 
32
21
  const log = new Logger("connection-manager");
33
22
 
34
23
  const DEFAULT_MAX_BOOTSTRAP_PEERS_ALLOWED = 1;
35
- const DEFAULT_MAX_DIAL_ATTEMPTS_FOR_PEER = 3;
36
- const DEFAULT_MAX_PARALLEL_DIALS = 3;
37
-
38
24
  const DEFAULT_PING_KEEP_ALIVE_SEC = 5 * 60;
39
25
  const DEFAULT_RELAY_KEEP_ALIVE_SEC = 5 * 60;
40
26
 
41
27
  type ConnectionManagerConstructorOptions = {
42
28
  libp2p: Libp2p;
29
+ events: IWakuEventEmitter;
43
30
  pubsubTopics: PubsubTopic[];
31
+ networkConfig: NetworkConfig;
44
32
  relay?: IRelay;
45
33
  config?: Partial<ConnectionManagerOptions>;
46
34
  };
47
35
 
48
- export class ConnectionManager
49
- extends TypedEventEmitter<IPeersByDiscoveryEvents & IConnectionStateEvents>
50
- implements IConnectionManager
51
- {
52
- // TODO(weboko): make it private
53
- public readonly pubsubTopics: PubsubTopic[];
36
+ export class ConnectionManager implements IConnectionManager {
37
+ private readonly pubsubTopics: PubsubTopic[];
38
+
39
+ private readonly keepAliveManager: KeepAliveManager;
40
+ private readonly discoveryDialer: DiscoveryDialer;
41
+ private readonly shardReader: ShardReader;
42
+ private readonly networkMonitor: NetworkMonitor;
43
+ private readonly connectionLimiter: ConnectionLimiter;
54
44
 
55
- private keepAliveManager: KeepAliveManager;
56
45
  private options: ConnectionManagerOptions;
57
46
  private libp2p: Libp2p;
58
- private dialAttemptsForPeer: Map<string, number> = new Map();
59
- private dialErrorsForPeer: Map<string, any> = new Map();
60
-
61
- private currentActiveParallelDialCount = 0;
62
- private pendingPeerDialQueue: Array<PeerId> = [];
63
-
64
- private isP2PNetworkConnected: boolean = false;
65
-
66
- public isConnected(): boolean {
67
- if (globalThis?.navigator && !globalThis?.navigator?.onLine) {
68
- return false;
69
- }
70
-
71
- return this.isP2PNetworkConnected;
72
- }
73
-
74
- public stop(): void {
75
- this.keepAliveManager.stopAll();
76
- this.libp2p.removeEventListener(
77
- "peer:connect",
78
- this.onEventHandlers["peer:connect"]
79
- );
80
- this.libp2p.removeEventListener(
81
- "peer:disconnect",
82
- this.onEventHandlers["peer:disconnect"]
83
- );
84
- this.libp2p.removeEventListener(
85
- "peer:discovery",
86
- this.onEventHandlers["peer:discovery"]
87
- );
88
- this.stopNetworkStatusListener();
89
- }
90
-
91
- public async dropConnection(peerId: PeerId): Promise<void> {
92
- try {
93
- this.keepAliveManager.stop(peerId);
94
- await this.libp2p.hangUp(peerId);
95
- log.info(`Dropped connection with peer ${peerId.toString()}`);
96
- } catch (error) {
97
- log.error(
98
- `Error dropping connection with peer ${peerId.toString()} - ${error}`
99
- );
100
- }
101
- }
102
-
103
- public async getPeersByDiscovery(): Promise<PeersByDiscoveryResult> {
104
- const peersDiscovered = await this.libp2p.peerStore.all();
105
- const peersConnected = this.libp2p
106
- .getConnections()
107
- .map((conn) => conn.remotePeer);
108
-
109
- const peersDiscoveredByBootstrap: Peer[] = [];
110
- const peersDiscoveredByPeerExchange: Peer[] = [];
111
- const peersDiscoveredByLocal: Peer[] = [];
112
-
113
- const peersConnectedByBootstrap: Peer[] = [];
114
- const peersConnectedByPeerExchange: Peer[] = [];
115
- const peersConnectedByLocal: Peer[] = [];
116
-
117
- for (const peer of peersDiscovered) {
118
- const tags = await this.getTagNamesForPeer(peer.id);
119
-
120
- if (tags.includes(Tags.BOOTSTRAP)) {
121
- peersDiscoveredByBootstrap.push(peer);
122
- } else if (tags.includes(Tags.PEER_EXCHANGE)) {
123
- peersDiscoveredByPeerExchange.push(peer);
124
- } else if (tags.includes(Tags.LOCAL)) {
125
- peersDiscoveredByLocal.push(peer);
126
- }
127
- }
128
-
129
- for (const peerId of peersConnected) {
130
- const peer = await this.libp2p.peerStore.get(peerId);
131
- const tags = await this.getTagNamesForPeer(peerId);
132
-
133
- if (tags.includes(Tags.BOOTSTRAP)) {
134
- peersConnectedByBootstrap.push(peer);
135
- } else if (tags.includes(Tags.PEER_EXCHANGE)) {
136
- peersConnectedByPeerExchange.push(peer);
137
- } else if (tags.includes(Tags.LOCAL)) {
138
- peersConnectedByLocal.push(peer);
139
- }
140
- }
141
-
142
- return {
143
- DISCOVERED: {
144
- [Tags.BOOTSTRAP]: peersDiscoveredByBootstrap,
145
- [Tags.PEER_EXCHANGE]: peersDiscoveredByPeerExchange,
146
- [Tags.LOCAL]: peersDiscoveredByLocal
147
- },
148
- CONNECTED: {
149
- [Tags.BOOTSTRAP]: peersConnectedByBootstrap,
150
- [Tags.PEER_EXCHANGE]: peersConnectedByPeerExchange,
151
- [Tags.LOCAL]: peersConnectedByLocal
152
- }
153
- };
154
- }
155
47
 
156
48
  public constructor(options: ConnectionManagerConstructorOptions) {
157
- super();
158
49
  this.libp2p = options.libp2p;
159
50
  this.pubsubTopics = options.pubsubTopics;
51
+
160
52
  this.options = {
161
- maxDialAttemptsForPeer: DEFAULT_MAX_DIAL_ATTEMPTS_FOR_PEER,
162
- maxBootstrapPeersAllowed: DEFAULT_MAX_BOOTSTRAP_PEERS_ALLOWED,
163
- maxParallelDials: DEFAULT_MAX_PARALLEL_DIALS,
53
+ maxBootstrapPeers: DEFAULT_MAX_BOOTSTRAP_PEERS_ALLOWED,
164
54
  pingKeepAlive: DEFAULT_PING_KEEP_ALIVE_SEC,
165
55
  relayKeepAlive: DEFAULT_RELAY_KEEP_ALIVE_SEC,
166
56
  ...options.config
@@ -175,584 +65,105 @@ export class ConnectionManager
175
65
  }
176
66
  });
177
67
 
178
- this.startEventListeners()
179
- .then(() => log.info(`Connection Manager is now running`))
180
- .catch((error) =>
181
- log.error(`Unexpected error while running service`, error)
182
- );
183
-
184
- // libp2p emits `peer:discovery` events during its initialization
185
- // which means that before the ConnectionManager is initialized, some peers may have been discovered
186
- // we will dial the peers in peerStore ONCE before we start to listen to the `peer:discovery` events within the ConnectionManager
187
- this.dialPeerStorePeers().catch((error) =>
188
- log.error(`Unexpected error while dialing peer store peers`, error)
189
- );
190
- }
191
-
192
- public async getConnectedPeers(codec?: string): Promise<Peer[]> {
193
- const peerIDs = this.libp2p.getPeers();
68
+ this.shardReader = new ShardReader({
69
+ libp2p: options.libp2p,
70
+ networkConfig: options.networkConfig
71
+ });
194
72
 
195
- if (peerIDs.length === 0) {
196
- return [];
197
- }
73
+ this.discoveryDialer = new DiscoveryDialer({
74
+ libp2p: options.libp2p,
75
+ shardReader: this.shardReader
76
+ });
198
77
 
199
- const peers = await Promise.all(
200
- peerIDs.map(async (id) => {
201
- try {
202
- return await this.libp2p.peerStore.get(id);
203
- } catch (e) {
204
- return null;
205
- }
206
- })
207
- );
78
+ this.networkMonitor = new NetworkMonitor({
79
+ libp2p: options.libp2p,
80
+ events: options.events
81
+ });
208
82
 
209
- return peers
210
- .filter((p) => !!p)
211
- .filter((p) => (codec ? (p as Peer).protocols.includes(codec) : true))
212
- .sort((left, right) => getPeerPing(left) - getPeerPing(right)) as Peer[];
83
+ this.connectionLimiter = new ConnectionLimiter({
84
+ libp2p: options.libp2p,
85
+ options: this.options
86
+ });
213
87
  }
214
88
 
215
- private async dialPeerStorePeers(): Promise<void> {
216
- const peerInfos = await this.libp2p.peerStore.all();
217
- const dialPromises = [];
218
- for (const peerInfo of peerInfos) {
219
- if (
220
- this.libp2p.getConnections().find((c) => c.remotePeer === peerInfo.id)
221
- )
222
- continue;
223
-
224
- dialPromises.push(this.attemptDial(peerInfo.id));
225
- }
226
- try {
227
- await Promise.all(dialPromises);
228
- } catch (error) {
229
- log.error(`Unexpected error while dialing peer store peers`, error);
230
- }
89
+ public start(): void {
90
+ this.networkMonitor.start();
91
+ this.discoveryDialer.start();
92
+ this.keepAliveManager.start();
93
+ this.connectionLimiter.start();
231
94
  }
232
95
 
233
- private async startEventListeners(): Promise<void> {
234
- this.startPeerDiscoveryListener();
235
- this.startPeerConnectionListener();
236
- this.startPeerDisconnectionListener();
237
-
238
- this.startNetworkStatusListener();
96
+ public stop(): void {
97
+ this.networkMonitor.stop();
98
+ this.discoveryDialer.stop();
99
+ this.keepAliveManager.stop();
100
+ this.connectionLimiter.stop();
239
101
  }
240
102
 
241
- /**
242
- * Attempts to establish a connection with a peer and set up specified protocols.
243
- * The method handles both PeerId and Multiaddr inputs, manages connection attempts,
244
- * and maintains the connection state.
245
- *
246
- * The dialing process includes:
247
- * 1. Converting input to dialable peer info
248
- * 2. Managing parallel dial attempts
249
- * 3. Attempting to establish protocol-specific connections
250
- * 4. Handling connection failures and retries
251
- * 5. Updating the peer store and connection state
252
- *
253
- * @param {PeerId | MultiaddrInput} peer - The peer to connect to, either as a PeerId or multiaddr
254
- * @param {string[]} [protocolCodecs] - Optional array of protocol-specific codec strings to establish
255
- * (e.g., for LightPush, Filter, Store protocols)
256
- *
257
- * @throws {Error} If the multiaddr is missing a peer ID
258
- * @throws {Error} If the maximum dial attempts are reached and the peer cannot be dialed
259
- * @throws {Error} If there's an error deleting an undialable peer from the peer store
260
- *
261
- * @example
262
- * ```typescript
263
- * // Dial using PeerId
264
- * await connectionManager.dialPeer(peerId);
265
- *
266
- * // Dial using multiaddr with specific protocols
267
- * await connectionManager.dialPeer(multiaddr, [
268
- * "/vac/waku/relay/2.0.0",
269
- * "/vac/waku/lightpush/2.0.0-beta1"
270
- * ]);
271
- * ```
272
- *
273
- * @remarks
274
- * - The method implements exponential backoff through multiple dial attempts
275
- * - Maintains a queue for parallel dial attempts (limited by maxParallelDials)
276
- * - Integrates with the KeepAliveManager for connection maintenance
277
- * - Updates the peer store and connection state after successful/failed attempts
278
- * - If all dial attempts fail, triggers DNS discovery as a fallback
279
- */
280
- public async dialPeer(peer: PeerId | MultiaddrInput): Promise<Connection> {
281
- let connection: Connection | undefined;
282
- let peerId: PeerId | undefined;
283
- const peerDialInfo = this.getDialablePeerInfo(peer);
284
- const peerIdStr = isPeerId(peerDialInfo)
285
- ? peerDialInfo.toString()
286
- : peerDialInfo.getPeerId()!;
287
-
288
- this.currentActiveParallelDialCount += 1;
289
- let dialAttempt = 0;
290
- while (dialAttempt < this.options.maxDialAttemptsForPeer) {
291
- try {
292
- log.info(`Dialing peer ${peerDialInfo} on attempt ${dialAttempt + 1}`);
293
- connection = await this.libp2p.dial(peerDialInfo);
294
- peerId = connection.remotePeer;
295
-
296
- const tags = await this.getTagNamesForPeer(peerId);
297
- // add tag to connection describing discovery mechanism
298
- // don't add duplicate tags
299
- this.libp2p.getConnections(peerId).forEach((conn) => {
300
- conn.tags = Array.from(new Set([...conn.tags, ...tags]));
301
- });
302
-
303
- // instead of deleting the peer from the peer store, we set the dial attempt to -1
304
- // this helps us keep track of peers that have been dialed before
305
- this.dialAttemptsForPeer.set(peerId.toString(), -1);
306
-
307
- // Dialing succeeded, break the loop
308
- this.keepAliveManager.start(peerId);
309
- break;
310
- } catch (error) {
311
- if (error instanceof AggregateError) {
312
- // Handle AggregateError
313
- log.error(`Error dialing peer ${peerIdStr} - ${error.errors}`);
314
- } else {
315
- // Handle generic error
316
- log.error(
317
- `Error dialing peer ${peerIdStr} - ${(error as any).message}`
318
- );
319
- }
320
- this.dialErrorsForPeer.set(peerIdStr, error);
321
-
322
- dialAttempt++;
323
- this.dialAttemptsForPeer.set(peerIdStr, dialAttempt);
324
- }
325
- }
326
-
327
- // Always decrease the active dial count and process the dial queue
328
- this.currentActiveParallelDialCount--;
329
- this.processDialQueue();
330
-
331
- // If max dial attempts reached and dialing failed, delete the peer
332
- if (dialAttempt === this.options.maxDialAttemptsForPeer) {
333
- try {
334
- const error = this.dialErrorsForPeer.get(peerIdStr);
335
-
336
- if (error) {
337
- let errorMessage;
338
- if (error instanceof AggregateError) {
339
- if (!error.errors) {
340
- log.warn(`No errors array found for AggregateError`);
341
- } else if (error.errors.length === 0) {
342
- log.warn(`Errors array is empty for AggregateError`);
343
- } else {
344
- errorMessage = JSON.stringify(error.errors[0]);
345
- }
346
- } else {
347
- errorMessage = error.message;
348
- }
349
-
350
- log.info(
351
- `Deleting undialable peer ${peerIdStr} from peer store. Reason: ${errorMessage}`
352
- );
353
- }
354
-
355
- this.dialErrorsForPeer.delete(peerIdStr);
356
- if (peerId) {
357
- await this.libp2p.peerStore.delete(peerId);
358
- }
359
-
360
- // if it was last available peer - attempt DNS discovery
361
- await this.attemptDnsDiscovery();
362
- } catch (error) {
363
- throw new Error(
364
- `Error deleting undialable peer ${peerIdStr} from peer store - ${error}`
365
- );
366
- }
367
- }
368
-
369
- if (!connection) {
370
- throw new Error(`Failed to dial peer ${peerDialInfo}`);
371
- }
372
-
373
- return connection;
103
+ public isConnected(): boolean {
104
+ return this.networkMonitor.isConnected();
374
105
  }
375
106
 
376
- /**
377
- * Dial a peer with specific protocols.
378
- * This method is a raw proxy to the libp2p dialProtocol method.
379
- * @param peer - The peer to connect to, either as a PeerId or multiaddr
380
- * @param protocolCodecs - Optional array of protocol-specific codec strings to establish
381
- * @returns A stream to the peer
382
- */
383
- public async rawDialPeerWithProtocols(
107
+ public async dial(
384
108
  peer: PeerId | MultiaddrInput,
385
109
  protocolCodecs: string[]
386
110
  ): Promise<Stream> {
387
- const peerDialInfo = this.getDialablePeerInfo(peer);
388
- return await this.libp2p.dialProtocol(peerDialInfo, protocolCodecs);
389
- }
390
-
391
- /**
392
- * Internal utility to extract a PeerId or Multiaddr from a peer input.
393
- * This is used internally by the connection manager to handle different peer input formats.
394
- * @internal
395
- */
396
- private getDialablePeerInfo(
397
- peer: PeerId | MultiaddrInput
398
- ): PeerId | Multiaddr {
399
- if (isPeerId(peer)) {
400
- return peer;
401
- } else {
402
- // peer is of MultiaddrInput type
403
- const ma = multiaddr(peer);
404
- const peerIdStr = ma.getPeerId();
405
- if (!peerIdStr) {
406
- throw new Error("Failed to dial multiaddr: missing peer ID");
407
- }
408
- return ma;
409
- }
111
+ const ma = mapToPeerIdOrMultiaddr(peer);
112
+ return this.libp2p.dialProtocol(ma, protocolCodecs);
410
113
  }
411
114
 
412
- private async attemptDnsDiscovery(): Promise<void> {
413
- if (this.libp2p.getConnections().length > 0) return;
414
- if ((await this.libp2p.peerStore.all()).length > 0) return;
115
+ public async hangUp(peer: PeerId | MultiaddrInput): Promise<boolean> {
116
+ const peerId = mapToPeerId(peer);
415
117
 
416
- log.info("Attempting to trigger DNS discovery.");
417
-
418
- const dnsDiscovery = Object.values(this.libp2p.components.components).find(
419
- (v: unknown) => {
420
- if (v && v.toString) {
421
- return v.toString().includes(DNS_DISCOVERY_TAG);
422
- }
423
-
424
- return false;
425
- }
426
- ) as DiscoveryTrigger;
427
-
428
- if (!dnsDiscovery) return;
429
-
430
- await dnsDiscovery.findPeers();
431
- }
432
-
433
- private processDialQueue(): void {
434
- if (
435
- this.pendingPeerDialQueue.length > 0 &&
436
- this.currentActiveParallelDialCount < this.options.maxParallelDials
437
- ) {
438
- const peerId = this.pendingPeerDialQueue.shift();
439
- if (!peerId) return;
440
- this.attemptDial(peerId).catch((error) => {
441
- log.error(error);
442
- });
443
- }
444
- }
445
-
446
- private startPeerDiscoveryListener(): void {
447
- this.libp2p.addEventListener(
448
- "peer:discovery",
449
- this.onEventHandlers["peer:discovery"]
450
- );
451
- }
452
-
453
- private startPeerConnectionListener(): void {
454
- this.libp2p.addEventListener(
455
- "peer:connect",
456
- this.onEventHandlers["peer:connect"]
457
- );
458
- }
459
-
460
- private startPeerDisconnectionListener(): void {
461
- // TODO: ensure that these following issues are updated and confirmed
462
- /**
463
- * NOTE: Event is not being emitted on closing nor losing a connection.
464
- * @see https://github.com/libp2p/js-libp2p/issues/939
465
- * @see https://github.com/status-im/js-waku/issues/252
466
- *
467
- * >This event will be triggered anytime we are disconnected from another peer,
468
- * >regardless of the circumstances of that disconnection.
469
- * >If we happen to have multiple connections to a peer,
470
- * >this event will **only** be triggered when the last connection is closed.
471
- * @see https://github.com/libp2p/js-libp2p/blob/bad9e8c0ff58d60a78314077720c82ae331cc55b/doc/API.md?plain=1#L2100
472
- */
473
- this.libp2p.addEventListener(
474
- "peer:disconnect",
475
- this.onEventHandlers["peer:disconnect"]
476
- );
477
- }
478
-
479
- public async attemptDial(peerId: PeerId): Promise<void> {
480
- if (!(await this.shouldDialPeer(peerId))) return;
481
-
482
- if (this.currentActiveParallelDialCount >= this.options.maxParallelDials) {
483
- this.pendingPeerDialQueue.push(peerId);
484
- return;
485
- }
486
-
487
- await this.dialPeer(peerId);
488
- }
489
-
490
- private onEventHandlers = {
491
- "peer:discovery": (evt: CustomEvent<PeerInfo>): void => {
492
- void (async () => {
493
- const { id: peerId } = evt.detail;
494
-
495
- await this.dispatchDiscoveryEvent(peerId);
496
-
497
- try {
498
- await this.attemptDial(peerId);
499
- } catch (error) {
500
- log.error(`Error dialing peer ${peerId.toString()} : ${error}`);
501
- }
502
- })();
503
- },
504
- "peer:connect": (evt: CustomEvent<PeerId>): void => {
505
- void (async () => {
506
- log.info(`Connected to peer ${evt.detail.toString()}`);
507
-
508
- const peerId = evt.detail;
509
-
510
- this.keepAliveManager.start(peerId);
511
-
512
- const isBootstrap = (await this.getTagNamesForPeer(peerId)).includes(
513
- Tags.BOOTSTRAP
514
- );
515
-
516
- if (isBootstrap) {
517
- const bootstrapConnections = this.libp2p
518
- .getConnections()
519
- .filter((conn) => conn.tags.includes(Tags.BOOTSTRAP));
520
-
521
- // If we have too many bootstrap connections, drop one
522
- if (
523
- bootstrapConnections.length > this.options.maxBootstrapPeersAllowed
524
- ) {
525
- await this.dropConnection(peerId);
526
- } else {
527
- this.dispatchEvent(
528
- new CustomEvent<PeerId>(
529
- EPeersByDiscoveryEvents.PEER_CONNECT_BOOTSTRAP,
530
- {
531
- detail: peerId
532
- }
533
- )
534
- );
535
- }
536
- } else {
537
- this.dispatchEvent(
538
- new CustomEvent<PeerId>(
539
- EPeersByDiscoveryEvents.PEER_CONNECT_PEER_EXCHANGE,
540
- {
541
- detail: peerId
542
- }
543
- )
544
- );
545
- }
546
-
547
- this.setP2PNetworkConnected();
548
- })();
549
- },
550
- "peer:disconnect": (evt: CustomEvent<PeerId>): void => {
551
- void (async () => {
552
- this.keepAliveManager.stop(evt.detail);
553
- this.setP2PNetworkDisconnected();
554
- })();
555
- },
556
- "browser:network": (): void => {
557
- this.dispatchWakuConnectionEvent();
558
- }
559
- };
560
-
561
- /**
562
- * Checks if the peer should be dialed based on the following conditions:
563
- * 1. If the peer is already connected, don't dial
564
- * 2. If the peer is not part of any of the configured pubsub topics, don't dial
565
- * 3. If the peer is not dialable based on bootstrap status, don't dial
566
- * 4. If the peer is already has an active dial attempt, or has been dialed before, don't dial it
567
- * @returns true if the peer should be dialed, false otherwise
568
- */
569
- private async shouldDialPeer(peerId: PeerId): Promise<boolean> {
570
- const isConnected = this.libp2p.getConnections(peerId).length > 0;
571
- if (isConnected) {
572
- log.warn(`Already connected to peer ${peerId.toString()}. Not dialing.`);
573
- return false;
574
- }
575
-
576
- const isSameShard = await this.isPeerOnSameShard(peerId);
577
- if (!isSameShard) {
578
- const shardInfo = await this.getPeerShardInfo(peerId);
579
-
580
- log.warn(
581
- `Discovered peer ${peerId.toString()} with ShardInfo ${shardInfo} is not part of any of the configured pubsub topics (${
582
- this.pubsubTopics
583
- }).
584
- Not dialing.`
585
- );
586
-
587
- return false;
588
- }
118
+ try {
119
+ await this.libp2p.hangUp(peerId);
589
120
 
590
- const isPreferredBasedOnBootstrap =
591
- await this.isPeerDialableBasedOnBootstrapStatus(peerId);
592
- if (!isPreferredBasedOnBootstrap) {
593
- log.warn(
594
- `Peer ${peerId.toString()} is not dialable based on bootstrap status. Not dialing.`
121
+ log.info(`Dropped connection with peer ${peerId.toString()}`);
122
+ return true;
123
+ } catch (error) {
124
+ log.error(
125
+ `Error dropping connection with peer ${peerId.toString()} - ${error}`
595
126
  );
596
- return false;
597
- }
598
127
 
599
- const hasBeenDialed = this.dialAttemptsForPeer.has(peerId.toString());
600
- if (hasBeenDialed) {
601
- log.warn(
602
- `Peer ${peerId.toString()} has already been attempted dial before, or already has a dial attempt in progress, skipping dial`
603
- );
604
128
  return false;
605
129
  }
606
-
607
- return true;
608
130
  }
609
131
 
610
- /**
611
- * Checks if the peer is dialable based on the following conditions:
612
- * 1. If the peer is a bootstrap peer, it is only dialable if the number of current bootstrap connections is less than the max allowed.
613
- * 2. If the peer is not a bootstrap peer
614
- */
615
- private async isPeerDialableBasedOnBootstrapStatus(
616
- peerId: PeerId
617
- ): Promise<boolean> {
618
- const tagNames = await this.getTagNamesForPeer(peerId);
619
-
620
- const isBootstrap = tagNames.some((tagName) => tagName === Tags.BOOTSTRAP);
132
+ public async getConnectedPeers(codec?: string): Promise<Peer[]> {
133
+ const peerIDs = this.libp2p.getPeers();
621
134
 
622
- if (!isBootstrap) {
623
- return true;
135
+ if (peerIDs.length === 0) {
136
+ return [];
624
137
  }
625
138
 
626
- const currentBootstrapConnections = this.libp2p
627
- .getConnections()
628
- .filter((conn) => {
629
- return conn.tags.find((name) => name === Tags.BOOTSTRAP);
630
- }).length;
631
-
632
- return currentBootstrapConnections < this.options.maxBootstrapPeersAllowed;
633
- }
634
-
635
- private async dispatchDiscoveryEvent(peerId: PeerId): Promise<void> {
636
- const isBootstrap = (await this.getTagNamesForPeer(peerId)).includes(
637
- Tags.BOOTSTRAP
638
- );
639
-
640
- this.dispatchEvent(
641
- new CustomEvent<PeerId>(
642
- isBootstrap
643
- ? EPeersByDiscoveryEvents.PEER_DISCOVERY_BOOTSTRAP
644
- : EPeersByDiscoveryEvents.PEER_DISCOVERY_PEER_EXCHANGE,
645
- {
646
- detail: peerId
139
+ const peers = await Promise.all(
140
+ peerIDs.map(async (id) => {
141
+ try {
142
+ return await this.libp2p.peerStore.get(id);
143
+ } catch (e) {
144
+ return null;
647
145
  }
648
- )
146
+ })
649
147
  );
650
- }
651
148
 
652
- /**
653
- * Fetches the tag names for a given peer
654
- */
655
- private async getTagNamesForPeer(peerId: PeerId): Promise<string[]> {
656
- try {
657
- const peer = await this.libp2p.peerStore.get(peerId);
658
- return Array.from(peer.tags.keys());
659
- } catch (error) {
660
- log.error(`Failed to get peer ${peerId}, error: ${error}`);
661
- return [];
662
- }
149
+ return peers
150
+ .filter((p) => !!p)
151
+ .filter((p) => (codec ? (p as Peer).protocols.includes(codec) : true))
152
+ .sort((left, right) => getPeerPing(left) - getPeerPing(right)) as Peer[];
663
153
  }
664
154
 
665
- public async isPeerOnSameShard(peerId: PeerId): Promise<boolean> {
666
- const shardInfo = await this.getPeerShardInfo(peerId);
667
-
668
- if (!shardInfo) {
669
- return true;
670
- }
671
-
672
- const pubsubTopics = shardInfoToPubsubTopics(shardInfo);
673
-
674
- const isTopicConfigured = pubsubTopics.some((topic) =>
675
- this.pubsubTopics.includes(topic)
676
- );
155
+ public isTopicConfigured(pubsubTopic: PubsubTopic): boolean {
156
+ return this.pubsubTopics.includes(pubsubTopic);
157
+ }
677
158
 
678
- return isTopicConfigured;
159
+ public async hasShardInfo(peerId: PeerId): Promise<boolean> {
160
+ return this.shardReader.hasShardInfo(peerId);
679
161
  }
680
162
 
681
- public async isPeerOnPubsubTopic(
163
+ public async isPeerOnTopic(
682
164
  peerId: PeerId,
683
165
  pubsubTopic: string
684
166
  ): Promise<boolean> {
685
- const shardInfo = await this.getPeerShardInfo(peerId);
686
-
687
- if (!shardInfo) {
688
- return true;
689
- }
690
-
691
- const pubsubTopics = shardInfoToPubsubTopics(shardInfo);
692
- return pubsubTopics.some((t) => t === pubsubTopic);
693
- }
694
-
695
- private async getPeerShardInfo(
696
- peerId: PeerId
697
- ): Promise<ShardInfo | undefined> {
698
- const peer = await this.libp2p.peerStore.get(peerId);
699
- const shardInfoBytes = peer.metadata.get("shardInfo");
700
- if (!shardInfoBytes) return undefined;
701
- return decodeRelayShard(shardInfoBytes);
702
- }
703
-
704
- private startNetworkStatusListener(): void {
705
- try {
706
- globalThis.addEventListener(
707
- "online",
708
- this.onEventHandlers["browser:network"]
709
- );
710
- globalThis.addEventListener(
711
- "offline",
712
- this.onEventHandlers["browser:network"]
713
- );
714
- } catch (err) {
715
- log.error(`Failed to start network listener: ${err}`);
716
- }
717
- }
718
-
719
- private stopNetworkStatusListener(): void {
720
- try {
721
- globalThis.removeEventListener(
722
- "online",
723
- this.onEventHandlers["browser:network"]
724
- );
725
- globalThis.removeEventListener(
726
- "offline",
727
- this.onEventHandlers["browser:network"]
728
- );
729
- } catch (err) {
730
- log.error(`Failed to stop network listener: ${err}`);
731
- }
732
- }
733
-
734
- private setP2PNetworkConnected(): void {
735
- if (!this.isP2PNetworkConnected) {
736
- this.isP2PNetworkConnected = true;
737
- this.dispatchWakuConnectionEvent();
738
- }
739
- }
740
-
741
- private setP2PNetworkDisconnected(): void {
742
- if (
743
- this.isP2PNetworkConnected &&
744
- this.libp2p.getConnections().length === 0
745
- ) {
746
- this.isP2PNetworkConnected = false;
747
- this.dispatchWakuConnectionEvent();
748
- }
749
- }
750
-
751
- private dispatchWakuConnectionEvent(): void {
752
- this.dispatchEvent(
753
- new CustomEvent<boolean>(EConnectionStateEvents.CONNECTION_STATUS, {
754
- detail: this.isConnected()
755
- })
756
- );
167
+ return this.shardReader.isPeerOnTopic(peerId, pubsubTopic);
757
168
  }
758
169
  }