@waku/core 0.0.23 → 0.0.25-a42b7be.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 (55) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/bundle/{base_protocol-84d9b670.js → base_protocol-4bcf7514.js} +194 -134
  3. package/bundle/{browser-bde977a3.js → browser-90197c87.js} +26 -1
  4. package/bundle/index-27b91e3b.js +31 -0
  5. package/bundle/index.js +19384 -2421
  6. package/bundle/lib/base_protocol.js +3 -2
  7. package/bundle/lib/message/version_0.js +3 -2
  8. package/bundle/lib/predefined_bootstrap_nodes.js +2 -0
  9. package/bundle/{version_0-74b4b9db.js → version_0-2f1176e3.js} +36 -24
  10. package/dist/.tsbuildinfo +1 -1
  11. package/dist/lib/connection_manager.d.ts +17 -4
  12. package/dist/lib/connection_manager.js +111 -45
  13. package/dist/lib/connection_manager.js.map +1 -1
  14. package/dist/lib/filter/index.js +53 -37
  15. package/dist/lib/filter/index.js.map +1 -1
  16. package/dist/lib/keep_alive_manager.d.ts +1 -0
  17. package/dist/lib/keep_alive_manager.js +42 -18
  18. package/dist/lib/keep_alive_manager.js.map +1 -1
  19. package/dist/lib/light_push/index.js +60 -38
  20. package/dist/lib/light_push/index.js.map +1 -1
  21. package/dist/lib/light_push/push_rpc.d.ts +1 -1
  22. package/dist/lib/light_push/push_rpc.js +2 -2
  23. package/dist/lib/message/version_0.d.ts +13 -13
  24. package/dist/lib/message/version_0.js +22 -20
  25. package/dist/lib/message/version_0.js.map +1 -1
  26. package/dist/lib/store/history_rpc.d.ts +1 -1
  27. package/dist/lib/store/history_rpc.js +1 -1
  28. package/dist/lib/store/index.d.ts +1 -1
  29. package/dist/lib/store/index.js +43 -17
  30. package/dist/lib/store/index.js.map +1 -1
  31. package/dist/lib/stream_manager.d.ts +1 -1
  32. package/dist/lib/stream_manager.js +8 -5
  33. package/dist/lib/stream_manager.js.map +1 -1
  34. package/dist/lib/wait_for_remote_peer.d.ts +2 -2
  35. package/dist/lib/wait_for_remote_peer.js +13 -11
  36. package/dist/lib/wait_for_remote_peer.js.map +1 -1
  37. package/dist/lib/waku.d.ts +4 -3
  38. package/dist/lib/waku.js +13 -11
  39. package/dist/lib/waku.js.map +1 -1
  40. package/package.json +1 -137
  41. package/src/lib/connection_manager.ts +156 -51
  42. package/src/lib/filter/index.ts +76 -40
  43. package/src/lib/keep_alive_manager.ts +53 -20
  44. package/src/lib/light_push/index.ts +74 -38
  45. package/src/lib/light_push/push_rpc.ts +2 -2
  46. package/src/lib/message/version_0.ts +25 -17
  47. package/src/lib/store/history_rpc.ts +2 -2
  48. package/src/lib/store/index.ts +60 -23
  49. package/src/lib/stream_manager.ts +12 -7
  50. package/src/lib/wait_for_remote_peer.ts +13 -11
  51. package/src/lib/waku.ts +12 -9
  52. package/dist/lib/push_or_init_map.d.ts +0 -1
  53. package/dist/lib/push_or_init_map.js +0 -9
  54. package/dist/lib/push_or_init_map.js.map +0 -1
  55. package/src/lib/push_or_init_map.ts +0 -13
@@ -1,7 +1,9 @@
1
1
  import type { PeerId } from "@libp2p/interface/peer-id";
2
2
  import type { PeerInfo } from "@libp2p/interface/peer-info";
3
3
  import type { Peer } from "@libp2p/interface/peer-store";
4
+ import type { PeerStore } from "@libp2p/interface/peer-store";
4
5
  import { CustomEvent, EventEmitter } from "@libp2p/interfaces/events";
6
+ import { decodeRelayShard } from "@waku/enr";
5
7
  import {
6
8
  ConnectionManagerOptions,
7
9
  EPeersByDiscoveryEvents,
@@ -9,14 +11,17 @@ import {
9
11
  IPeersByDiscoveryEvents,
10
12
  IRelay,
11
13
  KeepAliveOptions,
12
- PeersByDiscoveryResult
14
+ PeersByDiscoveryResult,
15
+ PubSubTopic,
16
+ ShardInfo
13
17
  } from "@waku/interfaces";
14
18
  import { Libp2p, Tags } from "@waku/interfaces";
15
- import debug from "debug";
19
+ import { shardInfoToPubSubTopics } from "@waku/utils";
20
+ import { Logger } from "@waku/utils";
16
21
 
17
22
  import { KeepAliveManager } from "./keep_alive_manager.js";
18
23
 
19
- const log = debug("waku:connection-manager");
24
+ const log = new Logger("connection-manager");
20
25
 
21
26
  export const DEFAULT_MAX_BOOTSTRAP_PEERS_ALLOWED = 1;
22
27
  export const DEFAULT_MAX_DIAL_ATTEMPTS_FOR_PEER = 3;
@@ -33,13 +38,14 @@ export class ConnectionManager
33
38
  private dialAttemptsForPeer: Map<string, number> = new Map();
34
39
  private dialErrorsForPeer: Map<string, any> = new Map();
35
40
 
36
- private currentActiveDialCount = 0;
41
+ private currentActiveParallelDialCount = 0;
37
42
  private pendingPeerDialQueue: Array<PeerId> = [];
38
43
 
39
44
  public static create(
40
45
  peerId: string,
41
46
  libp2p: Libp2p,
42
47
  keepAliveOptions: KeepAliveOptions,
48
+ pubsubTopics: PubSubTopic[],
43
49
  relay?: IRelay,
44
50
  options?: ConnectionManagerOptions
45
51
  ): ConnectionManager {
@@ -48,6 +54,7 @@ export class ConnectionManager
48
54
  instance = new ConnectionManager(
49
55
  libp2p,
50
56
  keepAliveOptions,
57
+ pubsubTopics,
51
58
  relay,
52
59
  options
53
60
  );
@@ -104,11 +111,13 @@ export class ConnectionManager
104
111
  private constructor(
105
112
  libp2p: Libp2p,
106
113
  keepAliveOptions: KeepAliveOptions,
114
+ private configuredPubSubTopics: PubSubTopic[],
107
115
  relay?: IRelay,
108
116
  options?: Partial<ConnectionManagerOptions>
109
117
  ) {
110
118
  super();
111
119
  this.libp2p = libp2p;
120
+ this.configuredPubSubTopics = configuredPubSubTopics;
112
121
  this.options = {
113
122
  maxDialAttemptsForPeer: DEFAULT_MAX_DIAL_ATTEMPTS_FOR_PEER,
114
123
  maxBootstrapPeersAllowed: DEFAULT_MAX_BOOTSTRAP_PEERS_ALLOWED,
@@ -119,14 +128,16 @@ export class ConnectionManager
119
128
  this.keepAliveManager = new KeepAliveManager(keepAliveOptions, relay);
120
129
 
121
130
  this.run()
122
- .then(() => log(`Connection Manager is now running`))
123
- .catch((error) => log(`Unexpected error while running service`, error));
131
+ .then(() => log.info(`Connection Manager is now running`))
132
+ .catch((error) =>
133
+ log.error(`Unexpected error while running service`, error)
134
+ );
124
135
 
125
136
  // libp2p emits `peer:discovery` events during its initialization
126
137
  // which means that before the ConnectionManager is initialized, some peers may have been discovered
127
138
  // we will dial the peers in peerStore ONCE before we start to listen to the `peer:discovery` events within the ConnectionManager
128
139
  this.dialPeerStorePeers().catch((error) =>
129
- log(`Unexpected error while dialing peer store peers`, error)
140
+ log.error(`Unexpected error while dialing peer store peers`, error)
130
141
  );
131
142
  }
132
143
 
@@ -144,7 +155,7 @@ export class ConnectionManager
144
155
  try {
145
156
  await Promise.all(dialPromises);
146
157
  } catch (error) {
147
- log(`Unexpected error while dialing peer store peers`, error);
158
+ log.error(`Unexpected error while dialing peer store peers`, error);
148
159
  }
149
160
  }
150
161
 
@@ -172,11 +183,13 @@ export class ConnectionManager
172
183
  }
173
184
 
174
185
  private async dialPeer(peerId: PeerId): Promise<void> {
175
- this.currentActiveDialCount += 1;
186
+ this.currentActiveParallelDialCount += 1;
176
187
  let dialAttempt = 0;
177
188
  while (dialAttempt < this.options.maxDialAttemptsForPeer) {
178
189
  try {
179
- log(`Dialing peer ${peerId.toString()} on attempt ${dialAttempt + 1}`);
190
+ log.info(
191
+ `Dialing peer ${peerId.toString()} on attempt ${dialAttempt + 1}`
192
+ );
180
193
  await this.libp2p.dial(peerId);
181
194
 
182
195
  const tags = await this.getTagNamesForPeer(peerId);
@@ -186,16 +199,21 @@ export class ConnectionManager
186
199
  conn.tags = Array.from(new Set([...conn.tags, ...tags]));
187
200
  });
188
201
 
189
- this.dialAttemptsForPeer.delete(peerId.toString());
202
+ // instead of deleting the peer from the peer store, we set the dial attempt to -1
203
+ // this helps us keep track of peers that have been dialed before
204
+ this.dialAttemptsForPeer.set(peerId.toString(), -1);
205
+
190
206
  // Dialing succeeded, break the loop
191
207
  break;
192
208
  } catch (error) {
193
209
  if (error instanceof AggregateError) {
194
210
  // Handle AggregateError
195
- log(`Error dialing peer ${peerId.toString()} - ${error.errors}`);
211
+ log.error(
212
+ `Error dialing peer ${peerId.toString()} - ${error.errors}`
213
+ );
196
214
  } else {
197
215
  // Handle generic error
198
- log(
216
+ log.error(
199
217
  `Error dialing peer ${peerId.toString()} - ${
200
218
  (error as any).message
201
219
  }`
@@ -209,7 +227,7 @@ export class ConnectionManager
209
227
  }
210
228
 
211
229
  // Always decrease the active dial count and process the dial queue
212
- this.currentActiveDialCount--;
230
+ this.currentActiveParallelDialCount--;
213
231
  this.processDialQueue();
214
232
 
215
233
  // If max dial attempts reached and dialing failed, delete the peer
@@ -217,16 +235,24 @@ export class ConnectionManager
217
235
  try {
218
236
  const error = this.dialErrorsForPeer.get(peerId.toString());
219
237
 
220
- let errorMessage;
221
- if (error instanceof AggregateError) {
222
- errorMessage = JSON.stringify(error.errors[0]);
223
- } else {
224
- errorMessage = error.message;
225
- }
238
+ if (error) {
239
+ let errorMessage;
240
+ if (error instanceof AggregateError) {
241
+ if (!error.errors) {
242
+ log.warn(`No errors array found for AggregateError`);
243
+ } else if (error.errors.length === 0) {
244
+ log.warn(`Errors array is empty for AggregateError`);
245
+ } else {
246
+ errorMessage = JSON.stringify(error.errors[0]);
247
+ }
248
+ } else {
249
+ errorMessage = error.message;
250
+ }
226
251
 
227
- log(
228
- `Deleting undialable peer ${peerId.toString()} from peer store. Error: ${errorMessage}`
229
- );
252
+ log.info(
253
+ `Deleting undialable peer ${peerId.toString()} from peer store. Reason: ${errorMessage}`
254
+ );
255
+ }
230
256
 
231
257
  this.dialErrorsForPeer.delete(peerId.toString());
232
258
  await this.libp2p.peerStore.delete(peerId);
@@ -242,9 +268,9 @@ export class ConnectionManager
242
268
  try {
243
269
  this.keepAliveManager.stop(peerId);
244
270
  await this.libp2p.hangUp(peerId);
245
- log(`Dropped connection with peer ${peerId.toString()}`);
271
+ log.info(`Dropped connection with peer ${peerId.toString()}`);
246
272
  } catch (error) {
247
- log(
273
+ log.error(
248
274
  `Error dropping connection with peer ${peerId.toString()} - ${error}`
249
275
  );
250
276
  }
@@ -253,12 +279,12 @@ export class ConnectionManager
253
279
  private processDialQueue(): void {
254
280
  if (
255
281
  this.pendingPeerDialQueue.length > 0 &&
256
- this.currentActiveDialCount < this.options.maxParallelDials
282
+ this.currentActiveParallelDialCount < this.options.maxParallelDials
257
283
  ) {
258
284
  const peerId = this.pendingPeerDialQueue.shift();
259
285
  if (!peerId) return;
260
286
  this.attemptDial(peerId).catch((error) => {
261
- log(error);
287
+ log.error(error);
262
288
  });
263
289
  }
264
290
  }
@@ -297,15 +323,15 @@ export class ConnectionManager
297
323
  }
298
324
 
299
325
  private async attemptDial(peerId: PeerId): Promise<void> {
300
- if (this.currentActiveDialCount >= this.options.maxParallelDials) {
326
+ if (!(await this.shouldDialPeer(peerId))) return;
327
+
328
+ if (this.currentActiveParallelDialCount >= this.options.maxParallelDials) {
301
329
  this.pendingPeerDialQueue.push(peerId);
302
330
  return;
303
331
  }
304
332
 
305
- if (!(await this.shouldDialPeer(peerId))) return;
306
-
307
333
  this.dialPeer(peerId).catch((err) => {
308
- throw `Error dialing peer ${peerId.toString()} : ${err}`;
334
+ log.error(`Error dialing peer ${peerId.toString()} : ${err}`);
309
335
  });
310
336
  }
311
337
 
@@ -314,25 +340,12 @@ export class ConnectionManager
314
340
  void (async () => {
315
341
  const { id: peerId } = evt.detail;
316
342
 
317
- const isBootstrap = (await this.getTagNamesForPeer(peerId)).includes(
318
- Tags.BOOTSTRAP
319
- );
320
-
321
- this.dispatchEvent(
322
- new CustomEvent<PeerId>(
323
- isBootstrap
324
- ? EPeersByDiscoveryEvents.PEER_DISCOVERY_BOOTSTRAP
325
- : EPeersByDiscoveryEvents.PEER_DISCOVERY_PEER_EXCHANGE,
326
- {
327
- detail: peerId
328
- }
329
- )
330
- );
343
+ await this.dispatchDiscoveryEvent(peerId);
331
344
 
332
345
  try {
333
346
  await this.attemptDial(peerId);
334
347
  } catch (error) {
335
- log(`Error dialing peer ${peerId.toString()} : ${error}`);
348
+ log.error(`Error dialing peer ${peerId.toString()} : ${error}`);
336
349
  }
337
350
  })();
338
351
  },
@@ -390,15 +403,63 @@ export class ConnectionManager
390
403
  };
391
404
 
392
405
  /**
393
- * Checks if the peer is dialable based on the following conditions:
394
- * 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.
395
- * 2. If the peer is not a bootstrap peer
406
+ * Checks if the peer should be dialed based on the following conditions:
407
+ * 1. If the peer is already connected, don't dial
408
+ * 2. If the peer is not part of any of the configured pubsub topics, don't dial
409
+ * 3. If the peer is not dialable based on bootstrap status, don't dial
410
+ * 4. If the peer is already has an active dial attempt, or has been dialed before, don't dial it
411
+ * @returns true if the peer should be dialed, false otherwise
396
412
  */
397
413
  private async shouldDialPeer(peerId: PeerId): Promise<boolean> {
414
+ // if we're already connected to the peer, don't dial
398
415
  const isConnected = this.libp2p.getConnections(peerId).length > 0;
416
+ if (isConnected) {
417
+ log.warn(`Already connected to peer ${peerId.toString()}. Not dialing.`);
418
+ return false;
419
+ }
399
420
 
400
- if (isConnected) return false;
421
+ // if the peer is not part of any of the configured pubsub topics, don't dial
422
+ if (!(await this.isPeerTopicConfigured(peerId))) {
423
+ const shardInfo = await this.getPeerShardInfo(
424
+ peerId,
425
+ this.libp2p.peerStore
426
+ );
427
+ log.warn(
428
+ `Discovered peer ${peerId.toString()} with ShardInfo ${shardInfo} is not part of any of the configured pubsub topics (${
429
+ this.configuredPubSubTopics
430
+ }).
431
+ Not dialing.`
432
+ );
433
+ return false;
434
+ }
401
435
 
436
+ // if the peer is not dialable based on bootstrap status, don't dial
437
+ if (!(await this.isPeerDialableBasedOnBootstrapStatus(peerId))) {
438
+ log.warn(
439
+ `Peer ${peerId.toString()} is not dialable based on bootstrap status. Not dialing.`
440
+ );
441
+ return false;
442
+ }
443
+
444
+ // If the peer is already already has an active dial attempt, or has been dialed before, don't dial it
445
+ if (this.dialAttemptsForPeer.has(peerId.toString())) {
446
+ log.warn(
447
+ `Peer ${peerId.toString()} has already been attempted dial before, or already has a dial attempt in progress, skipping dial`
448
+ );
449
+ return false;
450
+ }
451
+
452
+ return true;
453
+ }
454
+
455
+ /**
456
+ * Checks if the peer is dialable based on the following conditions:
457
+ * 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.
458
+ * 2. If the peer is not a bootstrap peer
459
+ */
460
+ private async isPeerDialableBasedOnBootstrapStatus(
461
+ peerId: PeerId
462
+ ): Promise<boolean> {
402
463
  const tagNames = await this.getTagNamesForPeer(peerId);
403
464
 
404
465
  const isBootstrap = tagNames.some((tagName) => tagName === Tags.BOOTSTRAP);
@@ -418,6 +479,23 @@ export class ConnectionManager
418
479
  return false;
419
480
  }
420
481
 
482
+ private async dispatchDiscoveryEvent(peerId: PeerId): Promise<void> {
483
+ const isBootstrap = (await this.getTagNamesForPeer(peerId)).includes(
484
+ Tags.BOOTSTRAP
485
+ );
486
+
487
+ this.dispatchEvent(
488
+ new CustomEvent<PeerId>(
489
+ isBootstrap
490
+ ? EPeersByDiscoveryEvents.PEER_DISCOVERY_BOOTSTRAP
491
+ : EPeersByDiscoveryEvents.PEER_DISCOVERY_PEER_EXCHANGE,
492
+ {
493
+ detail: peerId
494
+ }
495
+ )
496
+ );
497
+ }
498
+
421
499
  /**
422
500
  * Fetches the tag names for a given peer
423
501
  */
@@ -426,8 +504,35 @@ export class ConnectionManager
426
504
  const peer = await this.libp2p.peerStore.get(peerId);
427
505
  return Array.from(peer.tags.keys());
428
506
  } catch (error) {
429
- log(`Failed to get peer ${peerId}, error: ${error}`);
507
+ log.error(`Failed to get peer ${peerId}, error: ${error}`);
430
508
  return [];
431
509
  }
432
510
  }
511
+
512
+ private async isPeerTopicConfigured(peerId: PeerId): Promise<boolean> {
513
+ const shardInfo = await this.getPeerShardInfo(
514
+ peerId,
515
+ this.libp2p.peerStore
516
+ );
517
+
518
+ // If there's no shard information, simply return true
519
+ if (!shardInfo) return true;
520
+
521
+ const pubsubTopics = shardInfoToPubSubTopics(shardInfo);
522
+
523
+ const isTopicConfigured = pubsubTopics.some((topic) =>
524
+ this.configuredPubSubTopics.includes(topic)
525
+ );
526
+ return isTopicConfigured;
527
+ }
528
+
529
+ private async getPeerShardInfo(
530
+ peerId: PeerId,
531
+ peerStore: PeerStore
532
+ ): Promise<ShardInfo | undefined> {
533
+ const peer = await peerStore.get(peerId);
534
+ const shardInfoBytes = peer.metadata.get("shardInfo");
535
+ if (!shardInfoBytes) return undefined;
536
+ return decodeRelayShard(shardInfoBytes);
537
+ }
433
538
  }
@@ -17,8 +17,12 @@ import type {
17
17
  Unsubscribe
18
18
  } from "@waku/interfaces";
19
19
  import { WakuMessage } from "@waku/proto";
20
- import { groupByContentTopic, toAsyncIterator } from "@waku/utils";
21
- import debug from "debug";
20
+ import {
21
+ ensurePubsubTopicIsConfigured,
22
+ groupByContentTopic,
23
+ toAsyncIterator
24
+ } from "@waku/utils";
25
+ import { Logger } from "@waku/utils";
22
26
  import all from "it-all";
23
27
  import * as lp from "it-length-prefixed";
24
28
  import { pipe } from "it-pipe";
@@ -32,7 +36,7 @@ import {
32
36
  FilterSubscribeRpc
33
37
  } from "./filter_rpc.js";
34
38
 
35
- const log = debug("waku:filter:v2");
39
+ const log = new Logger("filter:v2");
36
40
 
37
41
  type SubscriptionCallback<T extends IDecodedMessage> = {
38
42
  decoders: IDecoder<T>[];
@@ -46,7 +50,7 @@ export const FilterCodecs = {
46
50
 
47
51
  class Subscription {
48
52
  private readonly peer: Peer;
49
- private readonly pubSubTopic: PubSubTopic;
53
+ private readonly pubsubTopic: PubSubTopic;
50
54
  private newStream: (peer: Peer) => Promise<Stream>;
51
55
 
52
56
  private subscriptionCallbacks: Map<
@@ -55,12 +59,12 @@ class Subscription {
55
59
  >;
56
60
 
57
61
  constructor(
58
- pubSubTopic: PubSubTopic,
62
+ pubsubTopic: PubSubTopic,
59
63
  remotePeer: Peer,
60
64
  newStream: (peer: Peer) => Promise<Stream>
61
65
  ) {
62
66
  this.peer = remotePeer;
63
- this.pubSubTopic = pubSubTopic;
67
+ this.pubsubTopic = pubsubTopic;
64
68
  this.newStream = newStream;
65
69
  this.subscriptionCallbacks = new Map();
66
70
  }
@@ -70,13 +74,23 @@ class Subscription {
70
74
  callback: Callback<T>
71
75
  ): Promise<void> {
72
76
  const decodersArray = Array.isArray(decoders) ? decoders : [decoders];
77
+
78
+ // check that all decoders are configured for the same pubsub topic as this subscription
79
+ decodersArray.forEach((decoder) => {
80
+ if (decoder.pubsubTopic !== this.pubsubTopic) {
81
+ throw new Error(
82
+ `Pubsub topic not configured: decoder is configured for pubsub topic ${decoder.pubsubTopic} but this subscription is for pubsub topic ${this.pubsubTopic}. Please create a new Subscription for the different pubsub topic.`
83
+ );
84
+ }
85
+ });
86
+
73
87
  const decodersGroupedByCT = groupByContentTopic(decodersArray);
74
88
  const contentTopics = Array.from(decodersGroupedByCT.keys());
75
89
 
76
90
  const stream = await this.newStream(this.peer);
77
91
 
78
92
  const request = FilterSubscribeRpc.createSubscribeRequest(
79
- this.pubSubTopic,
93
+ this.pubsubTopic,
80
94
  contentTopics
81
95
  );
82
96
 
@@ -89,6 +103,12 @@ class Subscription {
89
103
  async (source) => await all(source)
90
104
  );
91
105
 
106
+ if (!res || !res.length) {
107
+ throw Error(
108
+ `No response received for request ${request.requestId}: ${res}`
109
+ );
110
+ }
111
+
92
112
  const { statusCode, requestId, statusDesc } =
93
113
  FilterSubscribeResponse.decode(res[0].slice());
94
114
 
@@ -98,7 +118,7 @@ class Subscription {
98
118
  );
99
119
  }
100
120
 
101
- log(
121
+ log.info(
102
122
  "Subscribed to peer ",
103
123
  this.peer.id.toString(),
104
124
  "for content topics",
@@ -135,7 +155,7 @@ class Subscription {
135
155
  async unsubscribe(contentTopics: ContentTopic[]): Promise<void> {
136
156
  const stream = await this.newStream(this.peer);
137
157
  const unsubscribeRequest = FilterSubscribeRpc.createUnsubscribeRequest(
138
- this.pubSubTopic,
158
+ this.pubsubTopic,
139
159
  contentTopics
140
160
  );
141
161
 
@@ -164,6 +184,12 @@ class Subscription {
164
184
  async (source) => await all(source)
165
185
  );
166
186
 
187
+ if (!res || !res.length) {
188
+ throw Error(
189
+ `No response received for request ${request.requestId}: ${res}`
190
+ );
191
+ }
192
+
167
193
  const { statusCode, requestId, statusDesc } =
168
194
  FilterSubscribeResponse.decode(res[0].slice());
169
195
 
@@ -173,9 +199,9 @@ class Subscription {
173
199
  );
174
200
  }
175
201
 
176
- log("Ping successful");
202
+ log.info("Ping successful");
177
203
  } catch (error) {
178
- log("Error pinging: ", error);
204
+ log.error("Error pinging: ", error);
179
205
  throw new Error("Error pinging: " + error);
180
206
  }
181
207
  }
@@ -184,7 +210,7 @@ class Subscription {
184
210
  const stream = await this.newStream(this.peer);
185
211
 
186
212
  const request = FilterSubscribeRpc.createUnsubscribeAllRequest(
187
- this.pubSubTopic
213
+ this.pubsubTopic
188
214
  );
189
215
 
190
216
  try {
@@ -196,6 +222,12 @@ class Subscription {
196
222
  async (source) => await all(source)
197
223
  );
198
224
 
225
+ if (!res || !res.length) {
226
+ throw Error(
227
+ `No response received for request ${request.requestId}: ${res}`
228
+ );
229
+ }
230
+
199
231
  const { statusCode, requestId, statusDesc } =
200
232
  FilterSubscribeResponse.decode(res[0].slice());
201
233
 
@@ -206,7 +238,7 @@ class Subscription {
206
238
  }
207
239
 
208
240
  this.subscriptionCallbacks.clear();
209
- log("Unsubscribed from all content topics");
241
+ log.info("Unsubscribed from all content topics");
210
242
  } catch (error) {
211
243
  throw new Error("Error unsubscribing from all content topics: " + error);
212
244
  }
@@ -216,50 +248,53 @@ class Subscription {
216
248
  const contentTopic = message.contentTopic;
217
249
  const subscriptionCallback = this.subscriptionCallbacks.get(contentTopic);
218
250
  if (!subscriptionCallback) {
219
- log("No subscription callback available for ", contentTopic);
251
+ log.error("No subscription callback available for ", contentTopic);
220
252
  return;
221
253
  }
222
- await pushMessage(subscriptionCallback, this.pubSubTopic, message);
254
+ await pushMessage(subscriptionCallback, this.pubsubTopic, message);
223
255
  }
224
256
  }
225
257
 
226
258
  class Filter extends BaseProtocol implements IReceiver {
227
- private readonly options: ProtocolCreateOptions;
259
+ private readonly pubsubTopics: PubSubTopic[] = [];
228
260
  private activeSubscriptions = new Map<string, Subscription>();
229
261
  private readonly NUM_PEERS_PROTOCOL = 1;
230
262
 
231
263
  private getActiveSubscription(
232
- pubSubTopic: PubSubTopic,
264
+ pubsubTopic: PubSubTopic,
233
265
  peerIdStr: PeerIdStr
234
266
  ): Subscription | undefined {
235
- return this.activeSubscriptions.get(`${pubSubTopic}_${peerIdStr}`);
267
+ return this.activeSubscriptions.get(`${pubsubTopic}_${peerIdStr}`);
236
268
  }
237
269
 
238
270
  private setActiveSubscription(
239
- pubSubTopic: PubSubTopic,
271
+ pubsubTopic: PubSubTopic,
240
272
  peerIdStr: PeerIdStr,
241
273
  subscription: Subscription
242
274
  ): Subscription {
243
- this.activeSubscriptions.set(`${pubSubTopic}_${peerIdStr}`, subscription);
275
+ this.activeSubscriptions.set(`${pubsubTopic}_${peerIdStr}`, subscription);
244
276
  return subscription;
245
277
  }
246
278
 
247
279
  constructor(libp2p: Libp2p, options?: ProtocolCreateOptions) {
248
280
  super(FilterCodecs.SUBSCRIBE, libp2p.components);
249
281
 
282
+ this.pubsubTopics = options?.pubsubTopics || [DefaultPubSubTopic];
283
+
250
284
  libp2p.handle(FilterCodecs.PUSH, this.onRequest.bind(this)).catch((e) => {
251
- log("Failed to register ", FilterCodecs.PUSH, e);
285
+ log.error("Failed to register ", FilterCodecs.PUSH, e);
252
286
  });
253
287
 
254
288
  this.activeSubscriptions = new Map();
255
-
256
- this.options = options ?? {};
257
289
  }
258
290
 
259
- async createSubscription(pubSubTopic?: string): Promise<Subscription> {
260
- const _pubSubTopic =
261
- pubSubTopic ?? this.options.pubSubTopic ?? DefaultPubSubTopic;
291
+ async createSubscription(
292
+ pubsubTopic: string = DefaultPubSubTopic
293
+ ): Promise<Subscription> {
294
+ ensurePubsubTopicIsConfigured(pubsubTopic, this.pubsubTopics);
262
295
 
296
+ //TODO: get a relevant peer for the topic/shard
297
+ // https://github.com/waku-org/js-waku/pull/1586#discussion_r1336428230
263
298
  const peer = (
264
299
  await this.getPeers({
265
300
  maxBootstrapPeers: 1,
@@ -268,11 +303,11 @@ class Filter extends BaseProtocol implements IReceiver {
268
303
  )[0];
269
304
 
270
305
  const subscription =
271
- this.getActiveSubscription(_pubSubTopic, peer.id.toString()) ??
306
+ this.getActiveSubscription(pubsubTopic, peer.id.toString()) ??
272
307
  this.setActiveSubscription(
273
- _pubSubTopic,
308
+ pubsubTopic,
274
309
  peer.id.toString(),
275
- new Subscription(_pubSubTopic, peer, this.getStream.bind(this, peer))
310
+ new Subscription(pubsubTopic, peer, this.getStream.bind(this, peer))
276
311
  );
277
312
 
278
313
  return subscription;
@@ -319,7 +354,6 @@ class Filter extends BaseProtocol implements IReceiver {
319
354
  }
320
355
 
321
356
  private onRequest(streamData: IncomingStreamData): void {
322
- log("Receiving message push");
323
357
  try {
324
358
  pipe(streamData.stream, lp.decode, async (source) => {
325
359
  for await (const bytes of source) {
@@ -328,12 +362,12 @@ class Filter extends BaseProtocol implements IReceiver {
328
362
  const { pubsubTopic, wakuMessage } = response;
329
363
 
330
364
  if (!wakuMessage) {
331
- log("Received empty message");
365
+ log.error("Received empty message");
332
366
  return;
333
367
  }
334
368
 
335
369
  if (!pubsubTopic) {
336
- log("PubSub topic missing from push message");
370
+ log.error("PubSub topic missing from push message");
337
371
  return;
338
372
  }
339
373
 
@@ -344,7 +378,9 @@ class Filter extends BaseProtocol implements IReceiver {
344
378
  );
345
379
 
346
380
  if (!subscription) {
347
- log(`No subscription locally registered for topic ${pubsubTopic}`);
381
+ log.error(
382
+ `No subscription locally registered for topic ${pubsubTopic}`
383
+ );
348
384
  return;
349
385
  }
350
386
 
@@ -352,14 +388,14 @@ class Filter extends BaseProtocol implements IReceiver {
352
388
  }
353
389
  }).then(
354
390
  () => {
355
- log("Receiving pipe closed.");
391
+ log.info("Receiving pipe closed.");
356
392
  },
357
393
  (e) => {
358
- log("Error with receiving pipe", e);
394
+ log.error("Error with receiving pipe", e);
359
395
  }
360
396
  );
361
397
  } catch (e) {
362
- log("Error decoding message", e);
398
+ log.error("Error decoding message", e);
363
399
  }
364
400
  }
365
401
  }
@@ -372,21 +408,21 @@ export function wakuFilter(
372
408
 
373
409
  async function pushMessage<T extends IDecodedMessage>(
374
410
  subscriptionCallback: SubscriptionCallback<T>,
375
- pubSubTopic: PubSubTopic,
411
+ pubsubTopic: PubSubTopic,
376
412
  message: WakuMessage
377
413
  ): Promise<void> {
378
414
  const { decoders, callback } = subscriptionCallback;
379
415
 
380
416
  const { contentTopic } = message;
381
417
  if (!contentTopic) {
382
- log("Message has no content topic, skipping");
418
+ log.warn("Message has no content topic, skipping");
383
419
  return;
384
420
  }
385
421
 
386
422
  try {
387
423
  const decodePromises = decoders.map((dec) =>
388
424
  dec
389
- .fromProtoObj(pubSubTopic, message as IProtoMessage)
425
+ .fromProtoObj(pubsubTopic, message as IProtoMessage)
390
426
  .then((decoded) => decoded || Promise.reject("Decoding failed"))
391
427
  );
392
428
 
@@ -394,6 +430,6 @@ async function pushMessage<T extends IDecodedMessage>(
394
430
 
395
431
  await callback(decodedMessage);
396
432
  } catch (e) {
397
- log("Error decoding message", e);
433
+ log.error("Error decoding message", e);
398
434
  }
399
435
  }