@waku/core 0.0.26 → 0.0.27

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 +26 -0
  2. package/bundle/{base_protocol-pDODy0G6.js → base_protocol-LhsIWF3-.js} +137 -89
  3. package/bundle/{browser-mTOOnVZp.js → browser-BQyFvtq6.js} +501 -700
  4. package/bundle/{index-cmONXM-V.js → index-8YyfzF9R.js} +96 -41
  5. package/bundle/index.js +3033 -21649
  6. package/bundle/lib/base_protocol.js +3 -3
  7. package/bundle/lib/message/version_0.js +3 -3
  8. package/bundle/lib/predefined_bootstrap_nodes.js +1 -1
  9. package/bundle/{version_0-LQTFNC7k.js → version_0-FXfzO8Km.js} +1246 -2444
  10. package/dist/.tsbuildinfo +1 -1
  11. package/dist/index.d.ts +1 -5
  12. package/dist/index.js +1 -5
  13. package/dist/index.js.map +1 -1
  14. package/dist/lib/base_protocol.d.ts +13 -10
  15. package/dist/lib/base_protocol.js +38 -22
  16. package/dist/lib/base_protocol.js.map +1 -1
  17. package/dist/lib/connection_manager.d.ts +2 -2
  18. package/dist/lib/connection_manager.js +16 -6
  19. package/dist/lib/connection_manager.js.map +1 -1
  20. package/dist/lib/filter/index.d.ts +1 -1
  21. package/dist/lib/filter/index.js +140 -82
  22. package/dist/lib/filter/index.js.map +1 -1
  23. package/dist/lib/filterPeers.d.ts +8 -5
  24. package/dist/lib/filterPeers.js +12 -5
  25. package/dist/lib/filterPeers.js.map +1 -1
  26. package/dist/lib/keep_alive_manager.d.ts +2 -3
  27. package/dist/lib/keep_alive_manager.js.map +1 -1
  28. package/dist/lib/light_push/index.js +3 -6
  29. package/dist/lib/light_push/index.js.map +1 -1
  30. package/dist/lib/metadata/index.d.ts +2 -2
  31. package/dist/lib/metadata/index.js +25 -11
  32. package/dist/lib/metadata/index.js.map +1 -1
  33. package/dist/lib/store/index.js +1 -3
  34. package/dist/lib/store/index.js.map +1 -1
  35. package/dist/lib/stream_manager.d.ts +2 -2
  36. package/dist/lib/stream_manager.js.map +1 -1
  37. package/dist/lib/wait_for_remote_peer.d.ts +1 -1
  38. package/dist/lib/wait_for_remote_peer.js +40 -10
  39. package/dist/lib/wait_for_remote_peer.js.map +1 -1
  40. package/package.json +14 -12
  41. package/src/index.ts +1 -6
  42. package/src/lib/base_protocol.ts +59 -34
  43. package/src/lib/connection_manager.ts +17 -10
  44. package/src/lib/filter/index.ts +228 -137
  45. package/src/lib/filterPeers.ts +15 -7
  46. package/src/lib/keep_alive_manager.ts +2 -3
  47. package/src/lib/light_push/index.ts +11 -10
  48. package/src/lib/metadata/index.ts +51 -19
  49. package/src/lib/store/index.ts +3 -6
  50. package/src/lib/stream_manager.ts +2 -3
  51. package/src/lib/wait_for_remote_peer.ts +58 -12
  52. package/dist/lib/waku.d.ts +0 -57
  53. package/dist/lib/waku.js +0 -130
  54. package/dist/lib/waku.js.map +0 -1
  55. package/src/lib/waku.ts +0 -214
@@ -1,6 +1,6 @@
1
- import { Stream } from "@libp2p/interface/connection";
2
- import type { Peer } from "@libp2p/interface/peer-store";
3
- import type { IncomingStreamData } from "@libp2p/interface-internal/registrar";
1
+ import { Stream } from "@libp2p/interface";
2
+ import type { Peer } from "@libp2p/interface";
3
+ import type { IncomingStreamData } from "@libp2p/interface-internal";
4
4
  import type {
5
5
  Callback,
6
6
  ContentTopic,
@@ -11,13 +11,13 @@ import type {
11
11
  IProtoMessage,
12
12
  IReceiver,
13
13
  Libp2p,
14
- PeerIdStr,
15
14
  ProtocolCreateOptions,
16
15
  PubsubTopic,
17
16
  SingleShardInfo,
18
17
  Unsubscribe
19
18
  } from "@waku/interfaces";
20
19
  import { DefaultPubsubTopic } from "@waku/interfaces";
20
+ import { messageHashStr } from "@waku/message-hash";
21
21
  import { WakuMessage } from "@waku/proto";
22
22
  import {
23
23
  ensurePubsubTopicIsConfigured,
@@ -50,10 +50,14 @@ export const FilterCodecs = {
50
50
  PUSH: "/vac/waku/filter-push/2.0.0-beta1"
51
51
  };
52
52
 
53
+ /**
54
+ * A subscription object refers to a subscription to a given pubsub topic.
55
+ */
53
56
  class Subscription {
54
- private readonly peer: Peer;
57
+ readonly peers: Peer[];
55
58
  private readonly pubsubTopic: PubsubTopic;
56
59
  private newStream: (peer: Peer) => Promise<Stream>;
60
+ readonly receivedMessagesHashStr: string[] = [];
57
61
 
58
62
  private subscriptionCallbacks: Map<
59
63
  ContentTopic,
@@ -62,10 +66,10 @@ class Subscription {
62
66
 
63
67
  constructor(
64
68
  pubsubTopic: PubsubTopic,
65
- remotePeer: Peer,
69
+ remotePeers: Peer[],
66
70
  newStream: (peer: Peer) => Promise<Stream>
67
71
  ) {
68
- this.peer = remotePeer;
72
+ this.peers = remotePeers;
69
73
  this.pubsubTopic = pubsubTopic;
70
74
  this.newStream = newStream;
71
75
  this.subscriptionCallbacks = new Map();
@@ -89,53 +93,59 @@ class Subscription {
89
93
  const decodersGroupedByCT = groupByContentTopic(decodersArray);
90
94
  const contentTopics = Array.from(decodersGroupedByCT.keys());
91
95
 
92
- const stream = await this.newStream(this.peer);
96
+ const promises = this.peers.map(async (peer) => {
97
+ const stream = await this.newStream(peer);
93
98
 
94
- const request = FilterSubscribeRpc.createSubscribeRequest(
95
- this.pubsubTopic,
96
- contentTopics
97
- );
98
-
99
- try {
100
- const res = await pipe(
101
- [request.encode()],
102
- lp.encode,
103
- stream,
104
- lp.decode,
105
- async (source) => await all(source)
99
+ const request = FilterSubscribeRpc.createSubscribeRequest(
100
+ this.pubsubTopic,
101
+ contentTopics
106
102
  );
107
103
 
108
- if (!res || !res.length) {
109
- throw Error(
110
- `No response received for request ${request.requestId}: ${res}`
104
+ try {
105
+ const res = await pipe(
106
+ [request.encode()],
107
+ lp.encode,
108
+ stream,
109
+ lp.decode,
110
+ async (source) => await all(source)
111
111
  );
112
- }
113
112
 
114
- const { statusCode, requestId, statusDesc } =
115
- FilterSubscribeResponse.decode(res[0].slice());
113
+ if (!res || !res.length) {
114
+ throw Error(
115
+ `No response received for request ${request.requestId}: ${res}`
116
+ );
117
+ }
118
+
119
+ const { statusCode, requestId, statusDesc } =
120
+ FilterSubscribeResponse.decode(res[0].slice());
121
+
122
+ if (statusCode < 200 || statusCode >= 300) {
123
+ throw new Error(
124
+ `Filter subscribe request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
125
+ );
126
+ }
116
127
 
117
- if (statusCode < 200 || statusCode >= 300) {
128
+ log.info(
129
+ "Subscribed to peer ",
130
+ peer.id.toString(),
131
+ "for content topics",
132
+ contentTopics
133
+ );
134
+ } catch (e) {
118
135
  throw new Error(
119
- `Filter subscribe request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
136
+ "Error subscribing to peer: " +
137
+ peer.id.toString() +
138
+ " for content topics: " +
139
+ contentTopics +
140
+ ": " +
141
+ e
120
142
  );
121
143
  }
144
+ });
122
145
 
123
- log.info(
124
- "Subscribed to peer ",
125
- this.peer.id.toString(),
126
- "for content topics",
127
- contentTopics
128
- );
129
- } catch (e) {
130
- throw new Error(
131
- "Error subscribing to peer: " +
132
- this.peer.id.toString() +
133
- " for content topics: " +
134
- contentTopics +
135
- ": " +
136
- e
137
- );
138
- }
146
+ const results = await Promise.allSettled(promises);
147
+
148
+ this.handleErrors(results, "subscribe");
139
149
 
140
150
  // Save the callback functions by content topics so they
141
151
  // can easily be removed (reciprocally replaced) if `unsubscribe` (reciprocally `subscribe`)
@@ -155,133 +165,206 @@ class Subscription {
155
165
  }
156
166
 
157
167
  async unsubscribe(contentTopics: ContentTopic[]): Promise<void> {
158
- const stream = await this.newStream(this.peer);
159
- const unsubscribeRequest = FilterSubscribeRpc.createUnsubscribeRequest(
160
- this.pubsubTopic,
161
- contentTopics
162
- );
168
+ const promises = this.peers.map(async (peer) => {
169
+ const stream = await this.newStream(peer);
170
+ const unsubscribeRequest = FilterSubscribeRpc.createUnsubscribeRequest(
171
+ this.pubsubTopic,
172
+ contentTopics
173
+ );
163
174
 
164
- try {
165
- await pipe([unsubscribeRequest.encode()], lp.encode, stream.sink);
166
- } catch (error) {
167
- throw new Error("Error subscribing: " + error);
168
- }
175
+ try {
176
+ await pipe([unsubscribeRequest.encode()], lp.encode, stream.sink);
177
+ } catch (error) {
178
+ throw new Error("Error unsubscribing: " + error);
179
+ }
169
180
 
170
- contentTopics.forEach((contentTopic: string) => {
171
- this.subscriptionCallbacks.delete(contentTopic);
181
+ contentTopics.forEach((contentTopic: string) => {
182
+ this.subscriptionCallbacks.delete(contentTopic);
183
+ });
172
184
  });
185
+
186
+ const results = await Promise.allSettled(promises);
187
+
188
+ this.handleErrors(results, "unsubscribe");
173
189
  }
174
190
 
175
191
  async ping(): Promise<void> {
176
- const stream = await this.newStream(this.peer);
192
+ const promises = this.peers.map(async (peer) => {
193
+ const stream = await this.newStream(peer);
194
+
195
+ const request = FilterSubscribeRpc.createSubscriberPingRequest();
196
+
197
+ try {
198
+ const res = await pipe(
199
+ [request.encode()],
200
+ lp.encode,
201
+ stream,
202
+ lp.decode,
203
+ async (source) => await all(source)
204
+ );
177
205
 
178
- const request = FilterSubscribeRpc.createSubscriberPingRequest();
206
+ if (!res || !res.length) {
207
+ throw Error(
208
+ `No response received for request ${request.requestId}: ${res}`
209
+ );
210
+ }
179
211
 
180
- try {
181
- const res = await pipe(
182
- [request.encode()],
183
- lp.encode,
184
- stream,
185
- lp.decode,
186
- async (source) => await all(source)
187
- );
212
+ const { statusCode, requestId, statusDesc } =
213
+ FilterSubscribeResponse.decode(res[0].slice());
188
214
 
189
- if (!res || !res.length) {
190
- throw Error(
191
- `No response received for request ${request.requestId}: ${res}`
192
- );
215
+ if (statusCode < 200 || statusCode >= 300) {
216
+ throw new Error(
217
+ `Filter ping request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
218
+ );
219
+ }
220
+ log.info(`Ping successful for peer ${peer.id.toString()}`);
221
+ } catch (error) {
222
+ log.error("Error pinging: ", error);
223
+ throw error; // Rethrow the actual error instead of wrapping it
193
224
  }
225
+ });
194
226
 
195
- const { statusCode, requestId, statusDesc } =
196
- FilterSubscribeResponse.decode(res[0].slice());
227
+ const results = await Promise.allSettled(promises);
197
228
 
198
- if (statusCode < 200 || statusCode >= 300) {
199
- throw new Error(
200
- `Filter ping request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
201
- );
202
- }
203
-
204
- log.info("Ping successful");
205
- } catch (error) {
206
- log.error("Error pinging: ", error);
207
- throw new Error("Error pinging: " + error);
208
- }
229
+ this.handleErrors(results, "ping");
209
230
  }
210
231
 
211
232
  async unsubscribeAll(): Promise<void> {
212
- const stream = await this.newStream(this.peer);
213
-
214
- const request = FilterSubscribeRpc.createUnsubscribeAllRequest(
215
- this.pubsubTopic
216
- );
233
+ const promises = this.peers.map(async (peer) => {
234
+ const stream = await this.newStream(peer);
217
235
 
218
- try {
219
- const res = await pipe(
220
- [request.encode()],
221
- lp.encode,
222
- stream,
223
- lp.decode,
224
- async (source) => await all(source)
236
+ const request = FilterSubscribeRpc.createUnsubscribeAllRequest(
237
+ this.pubsubTopic
225
238
  );
226
239
 
227
- if (!res || !res.length) {
228
- throw Error(
229
- `No response received for request ${request.requestId}: ${res}`
240
+ try {
241
+ const res = await pipe(
242
+ [request.encode()],
243
+ lp.encode,
244
+ stream,
245
+ lp.decode,
246
+ async (source) => await all(source)
230
247
  );
231
- }
232
248
 
233
- const { statusCode, requestId, statusDesc } =
234
- FilterSubscribeResponse.decode(res[0].slice());
249
+ if (!res || !res.length) {
250
+ throw Error(
251
+ `No response received for request ${request.requestId}: ${res}`
252
+ );
253
+ }
254
+
255
+ const { statusCode, requestId, statusDesc } =
256
+ FilterSubscribeResponse.decode(res[0].slice());
235
257
 
236
- if (statusCode < 200 || statusCode >= 300) {
258
+ if (statusCode < 200 || statusCode >= 300) {
259
+ throw new Error(
260
+ `Filter unsubscribe all request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
261
+ );
262
+ }
263
+
264
+ this.subscriptionCallbacks.clear();
265
+ log.info(
266
+ `Unsubscribed from all content topics for pubsub topic ${this.pubsubTopic}`
267
+ );
268
+ } catch (error) {
237
269
  throw new Error(
238
- `Filter unsubscribe all request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
270
+ "Error unsubscribing from all content topics: " + error
239
271
  );
240
272
  }
273
+ });
241
274
 
242
- this.subscriptionCallbacks.clear();
243
- log.info("Unsubscribed from all content topics");
244
- } catch (error) {
245
- throw new Error("Error unsubscribing from all content topics: " + error);
246
- }
275
+ const results = await Promise.allSettled(promises);
276
+
277
+ this.handleErrors(results, "unsubscribeAll");
247
278
  }
248
279
 
249
280
  async processMessage(message: WakuMessage): Promise<void> {
250
- const contentTopic = message.contentTopic;
281
+ const hashedMessageStr = messageHashStr(
282
+ this.pubsubTopic,
283
+ message as IProtoMessage
284
+ );
285
+ if (this.receivedMessagesHashStr.includes(hashedMessageStr)) {
286
+ log.info("Message already received, skipping");
287
+ return;
288
+ }
289
+ this.receivedMessagesHashStr.push(hashedMessageStr);
290
+
291
+ const { contentTopic } = message;
251
292
  const subscriptionCallback = this.subscriptionCallbacks.get(contentTopic);
252
293
  if (!subscriptionCallback) {
253
294
  log.error("No subscription callback available for ", contentTopic);
254
295
  return;
255
296
  }
297
+ log.info(
298
+ "Processing message with content topic ",
299
+ contentTopic,
300
+ " on pubsub topic ",
301
+ this.pubsubTopic
302
+ );
256
303
  await pushMessage(subscriptionCallback, this.pubsubTopic, message);
257
304
  }
305
+
306
+ // Filter out only the rejected promises and extract & handle their reasons
307
+ private handleErrors(
308
+ results: PromiseSettledResult<void>[],
309
+ type: "ping" | "subscribe" | "unsubscribe" | "unsubscribeAll"
310
+ ): void {
311
+ const errors = results
312
+ .filter(
313
+ (result): result is PromiseRejectedResult =>
314
+ result.status === "rejected"
315
+ )
316
+ .map((rejectedResult) => rejectedResult.reason);
317
+
318
+ if (errors.length === this.peers.length) {
319
+ const errorCounts = new Map<string, number>();
320
+ // TODO: streamline error logging with https://github.com/orgs/waku-org/projects/2/views/1?pane=issue&itemId=42849952
321
+ errors.forEach((error) => {
322
+ const message = error instanceof Error ? error.message : String(error);
323
+ errorCounts.set(message, (errorCounts.get(message) || 0) + 1);
324
+ });
325
+
326
+ const uniqueErrorMessages = Array.from(
327
+ errorCounts,
328
+ ([message, count]) => `${message} (occurred ${count} times)`
329
+ ).join(", ");
330
+ throw new Error(`Error ${type} all peers: ${uniqueErrorMessages}`);
331
+ } else if (errors.length > 0) {
332
+ // TODO: handle renewing faulty peers with new peers (https://github.com/waku-org/js-waku/issues/1463)
333
+ log.warn(
334
+ `Some ${type} failed. These will be refreshed with new peers`,
335
+ errors
336
+ );
337
+ } else {
338
+ log.info(`${type} successful for all peers`);
339
+ }
340
+ }
258
341
  }
259
342
 
260
343
  class Filter extends BaseProtocol implements IReceiver {
261
- private readonly pubsubTopics: PubsubTopic[] = [];
262
344
  private activeSubscriptions = new Map<string, Subscription>();
263
- private readonly NUM_PEERS_PROTOCOL = 1;
264
345
 
265
346
  private getActiveSubscription(
266
- pubsubTopic: PubsubTopic,
267
- peerIdStr: PeerIdStr
347
+ pubsubTopic: PubsubTopic
268
348
  ): Subscription | undefined {
269
- return this.activeSubscriptions.get(`${pubsubTopic}_${peerIdStr}`);
349
+ return this.activeSubscriptions.get(pubsubTopic);
270
350
  }
271
351
 
272
352
  private setActiveSubscription(
273
353
  pubsubTopic: PubsubTopic,
274
- peerIdStr: PeerIdStr,
275
354
  subscription: Subscription
276
355
  ): Subscription {
277
- this.activeSubscriptions.set(`${pubsubTopic}_${peerIdStr}`, subscription);
356
+ this.activeSubscriptions.set(pubsubTopic, subscription);
278
357
  return subscription;
279
358
  }
280
359
 
281
360
  constructor(libp2p: Libp2p, options?: ProtocolCreateOptions) {
282
- super(FilterCodecs.SUBSCRIBE, libp2p.components);
283
-
284
- this.pubsubTopics = this.initializePubsubTopic(options);
361
+ super(
362
+ FilterCodecs.SUBSCRIBE,
363
+ libp2p.components,
364
+ log,
365
+ options!.pubsubTopics!,
366
+ options
367
+ );
285
368
 
286
369
  libp2p.handle(FilterCodecs.PUSH, this.onRequest.bind(this)).catch((e) => {
287
370
  log.error("Failed to register ", FilterCodecs.PUSH, e);
@@ -290,6 +373,12 @@ class Filter extends BaseProtocol implements IReceiver {
290
373
  this.activeSubscriptions = new Map();
291
374
  }
292
375
 
376
+ /**
377
+ * Creates a new subscription to the given pubsub topic.
378
+ * The subscription is made to multiple peers for decentralization.
379
+ * @param pubsubTopicShardInfo The pubsub topic to subscribe to.
380
+ * @returns The subscription object.
381
+ */
293
382
  async createSubscription(
294
383
  pubsubTopicShardInfo: SingleShardInfo | PubsubTopic = DefaultPubsubTopic
295
384
  ): Promise<Subscription> {
@@ -300,21 +389,24 @@ class Filter extends BaseProtocol implements IReceiver {
300
389
 
301
390
  ensurePubsubTopicIsConfigured(pubsubTopic, this.pubsubTopics);
302
391
 
303
- //TODO: get a relevant peer for the topic/shard
304
- // https://github.com/waku-org/js-waku/pull/1586#discussion_r1336428230
305
- const peer = (
306
- await this.getPeers({
307
- maxBootstrapPeers: 1,
308
- numPeers: this.NUM_PEERS_PROTOCOL
309
- })
310
- )[0];
392
+ const peers = await this.getPeers({
393
+ maxBootstrapPeers: 1,
394
+ numPeers: this.numPeersToUse
395
+ });
396
+ if (peers.length === 0) {
397
+ throw new Error("No peer found to initiate subscription.");
398
+ }
399
+
400
+ log.info(
401
+ `Creating filter subscription with ${peers.length} peers: `,
402
+ peers.map((peer) => peer.id.toString())
403
+ );
311
404
 
312
405
  const subscription =
313
- this.getActiveSubscription(pubsubTopic, peer.id.toString()) ??
406
+ this.getActiveSubscription(pubsubTopic) ??
314
407
  this.setActiveSubscription(
315
408
  pubsubTopic,
316
- peer.id.toString(),
317
- new Subscription(pubsubTopic, peer, this.getStream.bind(this, peer))
409
+ new Subscription(pubsubTopic, peers, this.getStream.bind(this))
318
410
  );
319
411
 
320
412
  return subscription;
@@ -361,8 +453,11 @@ class Filter extends BaseProtocol implements IReceiver {
361
453
  }
362
454
 
363
455
  private onRequest(streamData: IncomingStreamData): void {
456
+ const { connection, stream } = streamData;
457
+ const { remotePeer } = connection;
458
+ log.info(`Received message from ${remotePeer.toString()}`);
364
459
  try {
365
- pipe(streamData.stream, lp.decode, async (source) => {
460
+ pipe(stream, lp.decode, async (source) => {
366
461
  for await (const bytes of source) {
367
462
  const response = FilterPushRpc.decode(bytes.slice());
368
463
 
@@ -378,11 +473,7 @@ class Filter extends BaseProtocol implements IReceiver {
378
473
  return;
379
474
  }
380
475
 
381
- const peerIdStr = streamData.connection.remotePeer.toString();
382
- const subscription = this.getActiveSubscription(
383
- pubsubTopic,
384
- peerIdStr
385
- );
476
+ const subscription = this.getActiveSubscription(pubsubTopic);
386
477
 
387
478
  if (!subscription) {
388
479
  log.error(
@@ -408,7 +499,7 @@ class Filter extends BaseProtocol implements IReceiver {
408
499
  }
409
500
 
410
501
  export function wakuFilter(
411
- init: Partial<ProtocolCreateOptions> = {}
502
+ init: ProtocolCreateOptions = { pubsubTopics: [] }
412
503
  ): (libp2p: Libp2p) => IFilter {
413
504
  return (libp2p: Libp2p) => new Filter(libp2p, init);
414
505
  }
@@ -1,24 +1,32 @@
1
- import { Peer } from "@libp2p/interface/peer-store";
1
+ import { Peer } from "@libp2p/interface";
2
2
  import { Tags } from "@waku/interfaces";
3
3
 
4
4
  /**
5
- * Retrieves a list of peers based on the specified criteria.
5
+ * Retrieves a list of peers based on the specified criteria:
6
+ * 1. If numPeers is 0, return all peers
7
+ * 2. Bootstrap peers are prioritized
8
+ * 3. Non-bootstrap peers are randomly selected to fill up to numPeers
6
9
  *
7
10
  * @param peers - The list of peers to filter from.
8
- * @param numPeers - The total number of peers to retrieve. If 0, all peers are returned.
11
+ * @param numPeers - The total number of peers to retrieve. If 0, all peers are returned, irrespective of `maxBootstrapPeers`.
9
12
  * @param maxBootstrapPeers - The maximum number of bootstrap peers to retrieve.
10
- * @returns A Promise that resolves to an array of peers based on the specified criteria.
13
+ * @returns An array of peers based on the specified criteria.
11
14
  */
12
- export async function filterPeers(
15
+ export function filterPeersByDiscovery(
13
16
  peers: Peer[],
14
17
  numPeers: number,
15
18
  maxBootstrapPeers: number
16
- ): Promise<Peer[]> {
19
+ ): Peer[] {
17
20
  // Collect the bootstrap peers up to the specified maximum
18
- const bootstrapPeers = peers
21
+ let bootstrapPeers = peers
19
22
  .filter((peer) => peer.tags.has(Tags.BOOTSTRAP))
20
23
  .slice(0, maxBootstrapPeers);
21
24
 
25
+ // If numPeers is less than the number of bootstrap peers, adjust the bootstrapPeers array
26
+ if (numPeers > 0 && numPeers < bootstrapPeers.length) {
27
+ bootstrapPeers = bootstrapPeers.slice(0, numPeers);
28
+ }
29
+
22
30
  // Collect non-bootstrap peers
23
31
  const nonBootstrapPeers = peers.filter(
24
32
  (peer) => !peer.tags.has(Tags.BOOTSTRAP)
@@ -1,10 +1,9 @@
1
- import type { PeerId } from "@libp2p/interface/peer-id";
2
- import type { PeerStore } from "@libp2p/interface/peer-store";
1
+ import type { PeerId, PeerStore } from "@libp2p/interface";
2
+ import type { PingService } from "@libp2p/ping";
3
3
  import type { IRelay, PeerIdStr } from "@waku/interfaces";
4
4
  import type { KeepAliveOptions } from "@waku/interfaces";
5
5
  import { Logger, pubsubTopicToSingleShardInfo } from "@waku/utils";
6
6
  import { utf8ToBytes } from "@waku/utils/bytes";
7
- import type { PingService } from "libp2p/ping";
8
7
 
9
8
  import { createEncoder } from "./message/version_0.js";
10
9
 
@@ -1,12 +1,10 @@
1
- import type { Stream } from "@libp2p/interface/connection";
2
- import type { PeerId } from "@libp2p/interface/peer-id";
1
+ import type { PeerId, Stream } from "@libp2p/interface";
3
2
  import {
4
3
  IEncoder,
5
4
  ILightPush,
6
5
  IMessage,
7
6
  Libp2p,
8
7
  ProtocolCreateOptions,
9
- PubsubTopic,
10
8
  SendError,
11
9
  SendResult
12
10
  } from "@waku/interfaces";
@@ -44,12 +42,14 @@ type PreparePushMessageResult =
44
42
  * Implements the [Waku v2 Light Push protocol](https://rfc.vac.dev/spec/19/).
45
43
  */
46
44
  class LightPush extends BaseProtocol implements ILightPush {
47
- private readonly pubsubTopics: PubsubTopic[];
48
- private readonly NUM_PEERS_PROTOCOL = 1;
49
-
50
45
  constructor(libp2p: Libp2p, options?: ProtocolCreateOptions) {
51
- super(LightPushCodec, libp2p.components);
52
- this.pubsubTopics = this.initializePubsubTopic(options);
46
+ super(
47
+ LightPushCodec,
48
+ libp2p.components,
49
+ log,
50
+ options!.pubsubTopics!,
51
+ options
52
+ );
53
53
  }
54
54
 
55
55
  private async preparePushMessage(
@@ -108,10 +108,9 @@ class LightPush extends BaseProtocol implements ILightPush {
108
108
  };
109
109
  }
110
110
 
111
- //TODO: get a relevant peer for the topic/shard
112
111
  const peers = await this.getPeers({
113
112
  maxBootstrapPeers: 1,
114
- numPeers: this.NUM_PEERS_PROTOCOL
113
+ numPeers: this.numPeersToUse
115
114
  });
116
115
 
117
116
  if (!peers.length) {
@@ -177,6 +176,8 @@ class LightPush extends BaseProtocol implements ILightPush {
177
176
  });
178
177
 
179
178
  const results = await Promise.allSettled(promises);
179
+
180
+ // TODO: handle renewing faulty peers with new peers (https://github.com/waku-org/js-waku/issues/1463)
180
181
  const errors = results
181
182
  .filter(
182
183
  (