@waku/core 0.0.33-aeb05cd.0 → 0.0.33-b93134a.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.
@@ -156,110 +156,115 @@ function filterPeersByDiscovery(peers, numPeers, maxBootstrapPeers) {
156
156
  return selectedPeers;
157
157
  }
158
158
 
159
- function selectConnection(connections) {
160
- if (!connections.length)
161
- return;
162
- if (connections.length === 1)
163
- return connections[0];
164
- let latestConnection;
165
- connections.forEach((connection) => {
166
- if (connection.status === "open") {
167
- if (!latestConnection) {
168
- latestConnection = connection;
169
- }
170
- else if (connection.timeline.open > latestConnection.timeline.open) {
171
- latestConnection = connection;
172
- }
173
- }
174
- });
175
- return latestConnection;
159
+ function selectOpenConnection(connections) {
160
+ return connections
161
+ .filter((c) => c.status === "open")
162
+ .sort((left, right) => right.timeline.open - left.timeline.open)
163
+ .at(0);
176
164
  }
177
165
 
178
- const CONNECTION_TIMEOUT = 5_000;
179
- const RETRY_BACKOFF_BASE = 1_000;
180
- const MAX_RETRIES = 3;
181
166
  class StreamManager {
182
167
  multicodec;
183
168
  getConnections;
184
169
  addEventListener;
185
- streamPool;
186
170
  log;
171
+ ongoingCreation = new Set();
172
+ streamPool = new Map();
187
173
  constructor(multicodec, getConnections, addEventListener) {
188
174
  this.multicodec = multicodec;
189
175
  this.getConnections = getConnections;
190
176
  this.addEventListener = addEventListener;
191
177
  this.log = new Logger(`stream-manager:${multicodec}`);
192
- this.streamPool = new Map();
193
178
  this.addEventListener("peer:update", this.handlePeerUpdateStreamPool);
194
179
  }
195
180
  async getStream(peer) {
196
- const peerIdStr = peer.id.toString();
197
- const streamPromise = this.streamPool.get(peerIdStr);
198
- if (!streamPromise) {
199
- return this.createStream(peer);
200
- }
201
- this.streamPool.delete(peerIdStr);
202
- this.prepareStream(peer);
203
- try {
204
- const stream = await streamPromise;
205
- if (stream && stream.status !== "closed") {
206
- return stream;
207
- }
181
+ const peerId = peer.id.toString();
182
+ const scheduledStream = this.streamPool.get(peerId);
183
+ if (scheduledStream) {
184
+ this.streamPool.delete(peerId);
185
+ await scheduledStream;
208
186
  }
209
- catch (error) {
210
- this.log.warn(`Failed to get stream for ${peerIdStr} -- `, error);
211
- this.log.warn("Attempting to create a new stream for the peer");
187
+ const stream = this.getOpenStreamForCodec(peer.id);
188
+ if (stream) {
189
+ this.log.info(`Found existing stream peerId=${peer.id.toString()} multicodec=${this.multicodec}`);
190
+ return stream;
212
191
  }
213
192
  return this.createStream(peer);
214
193
  }
215
194
  async createStream(peer, retries = 0) {
216
195
  const connections = this.getConnections(peer.id);
217
- const connection = selectConnection(connections);
196
+ const connection = selectOpenConnection(connections);
218
197
  if (!connection) {
219
- throw new Error("Failed to get a connection to the peer");
198
+ throw new Error(`Failed to get a connection to the peer peerId=${peer.id.toString()} multicodec=${this.multicodec}`);
199
+ }
200
+ let lastError;
201
+ let stream;
202
+ for (let i = 0; i < retries + 1; i++) {
203
+ try {
204
+ this.log.info(`Attempting to create a stream for peerId=${peer.id.toString()} multicodec=${this.multicodec}`);
205
+ stream = await connection.newStream(this.multicodec);
206
+ this.log.info(`Created stream for peerId=${peer.id.toString()} multicodec=${this.multicodec}`);
207
+ break;
208
+ }
209
+ catch (error) {
210
+ lastError = error;
211
+ }
212
+ }
213
+ if (!stream) {
214
+ throw new Error(`Failed to create a new stream for ${peer.id.toString()} -- ` +
215
+ lastError);
216
+ }
217
+ return stream;
218
+ }
219
+ async createStreamWithLock(peer) {
220
+ const peerId = peer.id.toString();
221
+ if (this.ongoingCreation.has(peerId)) {
222
+ this.log.info(`Skipping creation of a stream due to lock for peerId=${peerId} multicodec=${this.multicodec}`);
223
+ return;
220
224
  }
221
225
  try {
222
- return await connection.newStream(this.multicodec);
226
+ this.ongoingCreation.add(peerId);
227
+ await this.createStream(peer);
223
228
  }
224
229
  catch (error) {
225
- if (retries < MAX_RETRIES) {
226
- const backoff = RETRY_BACKOFF_BASE * Math.pow(2, retries);
227
- await new Promise((resolve) => setTimeout(resolve, backoff));
228
- return this.createStream(peer, retries + 1);
229
- }
230
- throw new Error(`Failed to create a new stream for ${peer.id.toString()} -- ` + error);
230
+ this.log.error(`Failed to createStreamWithLock:`, error);
231
231
  }
232
- }
233
- prepareStream(peer) {
234
- const timeoutPromise = new Promise((resolve) => setTimeout(resolve, CONNECTION_TIMEOUT));
235
- const streamPromise = Promise.race([
236
- this.createStream(peer),
237
- timeoutPromise.then(() => {
238
- throw new Error("Connection timeout");
239
- })
240
- ]).catch((error) => {
241
- this.log.error(`Failed to prepare a new stream for ${peer.id.toString()} -- `, error);
242
- });
243
- this.streamPool.set(peer.id.toString(), streamPromise);
232
+ finally {
233
+ this.ongoingCreation.delete(peerId);
234
+ }
235
+ return;
244
236
  }
245
237
  handlePeerUpdateStreamPool = (evt) => {
246
238
  const { peer } = evt.detail;
247
- if (peer.protocols.includes(this.multicodec)) {
248
- const isConnected = this.isConnectedTo(peer.id);
249
- if (isConnected) {
250
- this.log.info(`Preemptively opening a stream to ${peer.id.toString()}`);
251
- this.prepareStream(peer);
252
- }
253
- else {
254
- const peerIdStr = peer.id.toString();
255
- this.streamPool.delete(peerIdStr);
256
- this.log.info(`Removed pending stream for disconnected peer ${peerIdStr}`);
257
- }
239
+ if (!peer.protocols.includes(this.multicodec)) {
240
+ return;
258
241
  }
242
+ const stream = this.getOpenStreamForCodec(peer.id);
243
+ if (stream) {
244
+ return;
245
+ }
246
+ this.scheduleNewStream(peer);
259
247
  };
260
- isConnectedTo(peerId) {
248
+ scheduleNewStream(peer) {
249
+ this.log.info(`Scheduling creation of a stream for peerId=${peer.id.toString()} multicodec=${this.multicodec}`);
250
+ // abandon previous attempt
251
+ if (this.streamPool.has(peer.id.toString())) {
252
+ this.streamPool.delete(peer.id.toString());
253
+ }
254
+ this.streamPool.set(peer.id.toString(), this.createStreamWithLock(peer));
255
+ }
256
+ getOpenStreamForCodec(peerId) {
261
257
  const connections = this.getConnections(peerId);
262
- return connections.some((connection) => connection.status === "open");
258
+ const connection = selectOpenConnection(connections);
259
+ if (!connection) {
260
+ return;
261
+ }
262
+ const stream = connection.streams.find((s) => s.protocol === this.multicodec);
263
+ const isStreamUnusable = ["done", "closed", "closing"].includes(stream?.writeStatus || "");
264
+ if (isStreamUnusable) {
265
+ return;
266
+ }
267
+ return stream;
263
268
  }
264
269
  }
265
270
 
@@ -287,20 +292,21 @@ class BaseProtocol {
287
292
  async getStream(peer) {
288
293
  return this.streamManager.getStream(peer);
289
294
  }
290
- //TODO: move to SDK
295
+ get peerStore() {
296
+ return this.components.peerStore;
297
+ }
291
298
  /**
292
299
  * Returns known peers from the address book (`libp2p.peerStore`) that support
293
300
  * the class protocol. Waku may or may not be currently connected to these
294
301
  * peers.
295
302
  */
296
303
  async allPeers() {
297
- return getPeersForProtocol(this.components.peerStore, [this.multicodec]);
304
+ return getPeersForProtocol(this.peerStore, [this.multicodec]);
298
305
  }
299
306
  async connectedPeers() {
300
307
  const peers = await this.allPeers();
301
308
  return peers.filter((peer) => {
302
- const connections = this.components.connectionManager.getConnections(peer.id);
303
- return connections.some((c) => c.streams.some((s) => s.protocol === this.multicodec));
309
+ return (this.components.connectionManager.getConnections(peer.id).length > 0);
304
310
  });
305
311
  }
306
312
  /**
@@ -316,11 +322,11 @@ class BaseProtocol {
316
322
  numPeers: 0
317
323
  }) {
318
324
  // Retrieve all connected peers that support the protocol & shard (if configured)
319
- const connectedPeersForProtocolAndShard = await getConnectedPeersForProtocolAndShard(this.components.connectionManager.getConnections(), this.components.peerStore, [this.multicodec], pubsubTopicsToShardInfo(this.pubsubTopics));
325
+ const connectedPeersForProtocolAndShard = await getConnectedPeersForProtocolAndShard(this.components.connectionManager.getConnections(), this.peerStore, [this.multicodec], pubsubTopicsToShardInfo(this.pubsubTopics));
320
326
  // Filter the peers based on discovery & number of peers requested
321
327
  const filteredPeers = filterPeersByDiscovery(connectedPeersForProtocolAndShard, numPeers, maxBootstrapPeers);
322
328
  // Sort the peers by latency
323
- const sortedFilteredPeers = await sortPeersByLatency(this.components.peerStore, filteredPeers);
329
+ const sortedFilteredPeers = await sortPeersByLatency(this.peerStore, filteredPeers);
324
330
  if (sortedFilteredPeers.length === 0) {
325
331
  this.log.warn("No peers found. Ensure you have a connection to the network.");
326
332
  }
package/bundle/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { v as version_0, e as encodingLength, a as encode$1, d as decode$1, M as MessagePush, F as FilterSubscribeRequest, b as FilterSubscribeResponse$1, P as PushRpc$1, c as PushResponse, S as StoreQueryRequest$1, f as StoreQueryResponse$1, g as createEncoder, W as WakuMetadataRequest, h as WakuMetadataResponse } from './version_0-BrbNEwD-.js';
2
2
  export { i as createDecoder } from './version_0-BrbNEwD-.js';
3
3
  import { a as allocUnsafe, b as alloc, L as Logger, P as ProtocolError, c as Protocols, u as utf8ToBytes, p as pubsubTopicToSingleShardInfo, T as Tags, E as EPeersByDiscoveryEvents, s as shardInfoToPubsubTopics, d as EConnectionStateEvents, H as HealthStatus, e as pubsubTopicsToShardInfo } from './index-tdQNdKHx.js';
4
- import { B as BaseProtocol, d as decodeRelayShard, e as encodeRelayShard } from './base_protocol-0xHh0_Hq.js';
5
- export { S as StreamManager } from './base_protocol-0xHh0_Hq.js';
4
+ import { B as BaseProtocol, d as decodeRelayShard, e as encodeRelayShard } from './base_protocol-1U9jKpZH.js';
5
+ export { S as StreamManager } from './base_protocol-1U9jKpZH.js';
6
6
 
7
7
  const MB = 1024 ** 2;
8
8
  const SIZE_CAP_IN_MB = 1;
@@ -1564,12 +1564,10 @@ const FilterCodecs = {
1564
1564
  };
1565
1565
  class FilterCore extends BaseProtocol {
1566
1566
  handleIncomingMessage;
1567
- handleError;
1568
1567
  pubsubTopics;
1569
- constructor(handleIncomingMessage, handleError, pubsubTopics, libp2p) {
1568
+ constructor(handleIncomingMessage, pubsubTopics, libp2p) {
1570
1569
  super(FilterCodecs.SUBSCRIBE, libp2p.components, log$6, pubsubTopics);
1571
1570
  this.handleIncomingMessage = handleIncomingMessage;
1572
- this.handleError = handleError;
1573
1571
  this.pubsubTopics = pubsubTopics;
1574
1572
  libp2p
1575
1573
  .handle(FilterCodecs.PUSH, this.onRequest.bind(this), {
@@ -1751,9 +1749,8 @@ class FilterCore extends BaseProtocol {
1751
1749
  }
1752
1750
  }).then(() => {
1753
1751
  log$6.info("Receiving pipe closed.");
1754
- }, async (e) => {
1755
- log$6.error("Error with receiving pipe", e, " -- ", "on peer ", connection.remotePeer.toString(), " -- ", "stream ", stream);
1756
- await this.handleError(e);
1752
+ }, (e) => {
1753
+ log$6.error("Error with receiving pipe", e);
1757
1754
  });
1758
1755
  }
1759
1756
  catch (e) {
@@ -3208,7 +3205,7 @@ class Metadata extends BaseProtocol {
3208
3205
  */
3209
3206
  async query(peerId) {
3210
3207
  const request = WakuMetadataRequest.encode(pubsubTopicsToShardInfo(this.pubsubTopics));
3211
- const peer = await this.libp2pComponents.peerStore.get(peerId);
3208
+ const peer = await this.peerStore.get(peerId);
3212
3209
  if (!peer) {
3213
3210
  return {
3214
3211
  shardInfo: null,
@@ -1,2 +1,2 @@
1
1
  import '../index-tdQNdKHx.js';
2
- export { B as BaseProtocol } from '../base_protocol-0xHh0_Hq.js';
2
+ export { B as BaseProtocol } from '../base_protocol-1U9jKpZH.js';