@waku/discovery 0.0.10 → 0.0.11-158e115.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.
package/bundle/index.js CHANGED
@@ -286,6 +286,12 @@ var ProtocolError;
286
286
  ProtocolError["INVALID_DECODER_TOPICS"] = "Invalid decoder topics";
287
287
  })(ProtocolError || (ProtocolError = {}));
288
288
 
289
+ var WakuEventType;
290
+ (function (WakuEventType) {
291
+ WakuEventType["Connection"] = "waku:connection";
292
+ WakuEventType["Health"] = "waku:health";
293
+ })(WakuEventType || (WakuEventType = {}));
294
+
289
295
  // Peer tags
290
296
  var Tags;
291
297
  (function (Tags) {
@@ -316,6 +322,25 @@ function isDefined(value) {
316
322
  return Boolean(value);
317
323
  }
318
324
 
325
+ /**
326
+ * Return pseudo random subset of the input.
327
+ */
328
+ function shuffle(arr) {
329
+ if (arr.length <= 1) {
330
+ return arr;
331
+ }
332
+ const randInt = () => {
333
+ return Math.floor(Math.random() * Math.floor(arr.length));
334
+ };
335
+ for (let i = 0; i < arr.length; i++) {
336
+ const j = randInt();
337
+ const tmp = arr[i];
338
+ arr[i] = arr[j];
339
+ arr[j] = tmp;
340
+ }
341
+ return arr;
342
+ }
343
+
319
344
  const crypto$2 = typeof globalThis === 'object' && 'crypto' in globalThis ? globalThis.crypto : undefined;
320
345
 
321
346
  /**
@@ -3151,11 +3176,6 @@ const enrTree = {
3151
3176
  const DEFAULT_BOOTSTRAP_TAG_NAME = Tags.BOOTSTRAP;
3152
3177
  const DEFAULT_BOOTSTRAP_TAG_VALUE = 50;
3153
3178
  const DEFAULT_BOOTSTRAP_TAG_TTL = 100_000_000;
3154
- const DEFAULT_NODE_REQUIREMENTS = {
3155
- store: 1,
3156
- filter: 2,
3157
- lightPush: 2
3158
- };
3159
3179
 
3160
3180
  // Maximum encoded size of an ENR
3161
3181
  const ERR_INVALID_ID = "Invalid record id";
@@ -15099,188 +15119,63 @@ class ENRTree {
15099
15119
 
15100
15120
  const log$5 = new Logger$1("discovery:fetch_nodes");
15101
15121
  /**
15102
- * Fetch nodes using passed [[getNode]] until all wanted capabilities are
15103
- * fulfilled or the number of [[getNode]] call exceeds the sum of
15104
- * [[wantedNodeCapabilityCount]] plus [[errorTolerance]].
15122
+ * Fetch nodes using passed [[getNode]] until it has been called [[maxGet]]
15123
+ * times, or it has returned empty or duplicate results more than [[maxErrors]]
15124
+ * times.
15105
15125
  */
15106
- async function fetchNodesUntilCapabilitiesFulfilled(wantedNodeCapabilityCount, errorTolerance, getNode) {
15107
- const wanted = {
15108
- relay: wantedNodeCapabilityCount.relay ?? 0,
15109
- store: wantedNodeCapabilityCount.store ?? 0,
15110
- filter: wantedNodeCapabilityCount.filter ?? 0,
15111
- lightPush: wantedNodeCapabilityCount.lightPush ?? 0
15112
- };
15113
- const maxSearches = wanted.relay + wanted.store + wanted.filter + wanted.lightPush;
15114
- const actual = {
15115
- relay: 0,
15116
- store: 0,
15117
- filter: 0,
15118
- lightPush: 0
15119
- };
15126
+ async function* fetchNodes(getNode, maxGet = 10, maxErrors = 3) {
15127
+ const peerNodeIds = new Set();
15120
15128
  let totalSearches = 0;
15121
- const peers = [];
15122
- while (!isSatisfied(wanted, actual) &&
15123
- totalSearches < maxSearches + errorTolerance) {
15124
- const peer = await getNode();
15125
- if (peer && isNewPeer(peer, peers)) {
15126
- // ENRs without a waku2 key are ignored.
15127
- if (peer.waku2) {
15128
- if (helpsSatisfyCapabilities(peer.waku2, wanted, actual)) {
15129
- addCapabilities(peer.waku2, actual);
15130
- peers.push(peer);
15131
- }
15132
- }
15133
- log$5.info(`got new peer candidate from DNS address=${peer.nodeId}@${peer.ip}`);
15134
- }
15129
+ let erroneousSearches = 0;
15130
+ while (totalSearches < maxGet &&
15131
+ erroneousSearches < maxErrors // Allows a couple of empty results before calling it quit
15132
+ ) {
15135
15133
  totalSearches++;
15136
- }
15137
- return peers;
15138
- }
15139
- /**
15140
- * Fetch nodes using passed [[getNode]] until all wanted capabilities are
15141
- * fulfilled or the number of [[getNode]] call exceeds the sum of
15142
- * [[wantedNodeCapabilityCount]] plus [[errorTolerance]].
15143
- */
15144
- async function* yieldNodesUntilCapabilitiesFulfilled(wantedNodeCapabilityCount, errorTolerance, getNode) {
15145
- const wanted = {
15146
- relay: wantedNodeCapabilityCount.relay ?? 0,
15147
- store: wantedNodeCapabilityCount.store ?? 0,
15148
- filter: wantedNodeCapabilityCount.filter ?? 0,
15149
- lightPush: wantedNodeCapabilityCount.lightPush ?? 0
15150
- };
15151
- const maxSearches = wanted.relay + wanted.store + wanted.filter + wanted.lightPush;
15152
- const actual = {
15153
- relay: 0,
15154
- store: 0,
15155
- filter: 0,
15156
- lightPush: 0
15157
- };
15158
- let totalSearches = 0;
15159
- const peerNodeIds = new Set();
15160
- while (!isSatisfied(wanted, actual) &&
15161
- totalSearches < maxSearches + errorTolerance) {
15162
15134
  const peer = await getNode();
15163
- if (peer && peer.nodeId && !peerNodeIds.has(peer.nodeId)) {
15135
+ if (!peer || !peer.nodeId) {
15136
+ erroneousSearches++;
15137
+ continue;
15138
+ }
15139
+ if (!peerNodeIds.has(peer.nodeId)) {
15164
15140
  peerNodeIds.add(peer.nodeId);
15165
15141
  // ENRs without a waku2 key are ignored.
15166
15142
  if (peer.waku2) {
15167
- if (helpsSatisfyCapabilities(peer.waku2, wanted, actual)) {
15168
- addCapabilities(peer.waku2, actual);
15169
- yield peer;
15170
- }
15143
+ yield peer;
15171
15144
  }
15172
15145
  log$5.info(`got new peer candidate from DNS address=${peer.nodeId}@${peer.ip}`);
15173
15146
  }
15174
- totalSearches++;
15175
15147
  }
15176
15148
  }
15177
- function isSatisfied(wanted, actual) {
15178
- return (actual.relay >= wanted.relay &&
15179
- actual.store >= wanted.store &&
15180
- actual.filter >= wanted.filter &&
15181
- actual.lightPush >= wanted.lightPush);
15182
- }
15183
- function isNewPeer(peer, peers) {
15184
- if (!peer.nodeId)
15185
- return false;
15186
- for (const existingPeer of peers) {
15187
- if (peer.nodeId === existingPeer.nodeId) {
15188
- return false;
15189
- }
15190
- }
15191
- return true;
15192
- }
15193
- function addCapabilities(node, total) {
15194
- if (node.relay)
15195
- total.relay += 1;
15196
- if (node.store)
15197
- total.store += 1;
15198
- if (node.filter)
15199
- total.filter += 1;
15200
- if (node.lightPush)
15201
- total.lightPush += 1;
15202
- }
15203
- /**
15204
- * Checks if the proposed ENR [[node]] helps satisfy the [[wanted]] capabilities,
15205
- * considering the [[actual]] capabilities of nodes retrieved so far..
15206
- *
15207
- * @throws If the function is called when the wanted capabilities are already fulfilled.
15208
- */
15209
- function helpsSatisfyCapabilities(node, wanted, actual) {
15210
- if (isSatisfied(wanted, actual)) {
15211
- throw "Internal Error: Waku2 wanted capabilities are already fulfilled";
15212
- }
15213
- const missing = missingCapabilities(wanted, actual);
15214
- return ((missing.relay && node.relay) ||
15215
- (missing.store && node.store) ||
15216
- (missing.filter && node.filter) ||
15217
- (missing.lightPush && node.lightPush));
15218
- }
15219
- /**
15220
- * Return a [[Waku2]] Object for which capabilities are set to true if they are
15221
- * [[wanted]] yet missing from [[actual]].
15222
- */
15223
- function missingCapabilities(wanted, actual) {
15224
- return {
15225
- relay: actual.relay < wanted.relay,
15226
- store: actual.store < wanted.store,
15227
- filter: actual.filter < wanted.filter,
15228
- lightPush: actual.lightPush < wanted.lightPush
15229
- };
15230
- }
15231
15149
 
15232
15150
  const log$4 = new Logger$1("discovery:dns");
15233
15151
  class DnsNodeDiscovery {
15234
15152
  dns;
15235
15153
  _DNSTreeCache;
15236
- _errorTolerance = 10;
15237
15154
  static async dnsOverHttp(dnsClient) {
15238
15155
  if (!dnsClient) {
15239
15156
  dnsClient = await DnsOverHttps.create();
15240
15157
  }
15241
15158
  return new DnsNodeDiscovery(dnsClient);
15242
15159
  }
15243
- /**
15244
- * Returns a list of verified peers listed in an EIP-1459 DNS tree. Method may
15245
- * return fewer peers than requested if @link wantedNodeCapabilityCount requires
15246
- * larger quantity of peers than available or the number of errors/duplicate
15247
- * peers encountered by randomized search exceeds the sum of the fields of
15248
- * @link wantedNodeCapabilityCount plus the @link _errorTolerance factor.
15249
- */
15250
- async getPeers(enrTreeUrls, wantedNodeCapabilityCount) {
15251
- const networkIndex = Math.floor(Math.random() * enrTreeUrls.length);
15252
- const { publicKey, domain } = ENRTree.parseTree(enrTreeUrls[networkIndex]);
15253
- const context = {
15254
- domain,
15255
- publicKey,
15256
- visits: {}
15257
- };
15258
- const peers = await fetchNodesUntilCapabilitiesFulfilled(wantedNodeCapabilityCount, this._errorTolerance, () => this._search(domain, context));
15259
- log$4.info("retrieved peers: ", peers.map((peer) => {
15260
- return {
15261
- id: peer.peerId?.toString(),
15262
- multiaddrs: peer.multiaddrs?.map((ma) => ma.toString())
15263
- };
15264
- }));
15265
- return peers;
15266
- }
15267
15160
  constructor(dns) {
15268
15161
  this._DNSTreeCache = {};
15269
15162
  this.dns = dns;
15270
15163
  }
15271
15164
  /**
15272
- * {@inheritDoc getPeers}
15165
+ * Retrieve the next peers from the passed [[enrTreeUrls]],
15273
15166
  */
15274
- async *getNextPeer(enrTreeUrls, wantedNodeCapabilityCount) {
15275
- const networkIndex = Math.floor(Math.random() * enrTreeUrls.length);
15276
- const { publicKey, domain } = ENRTree.parseTree(enrTreeUrls[networkIndex]);
15277
- const context = {
15278
- domain,
15279
- publicKey,
15280
- visits: {}
15281
- };
15282
- for await (const peer of yieldNodesUntilCapabilitiesFulfilled(wantedNodeCapabilityCount, this._errorTolerance, () => this._search(domain, context))) {
15283
- yield peer;
15167
+ async *getNextPeer(enrTreeUrls) {
15168
+ // Shuffle the ENR Trees so that not all clients connect to same nodes first.
15169
+ for (const enrTreeUrl of shuffle(enrTreeUrls)) {
15170
+ const { publicKey, domain } = ENRTree.parseTree(enrTreeUrl);
15171
+ const context = {
15172
+ domain,
15173
+ publicKey,
15174
+ visits: {}
15175
+ };
15176
+ for await (const peer of fetchNodes(() => this._search(domain, context))) {
15177
+ yield peer;
15178
+ }
15284
15179
  }
15285
15180
  }
15286
15181
  /**
@@ -15338,7 +15233,7 @@ class DnsNodeDiscovery {
15338
15233
  throw new Error("Received empty result array while fetching TXT record");
15339
15234
  if (!response[0].length)
15340
15235
  throw new Error("Received empty TXT record");
15341
- // Branch entries can be an array of strings of comma delimited subdomains, with
15236
+ // Branch entries can be an array of strings of comma-delimited subdomains, with
15342
15237
  // some subdomain strings split across the array elements
15343
15238
  const result = response.join("");
15344
15239
  this._DNSTreeCache[subdomain] = result;
@@ -15414,9 +15309,8 @@ class PeerDiscoveryDns extends TypedEventEmitter {
15414
15309
  let { enrUrls } = this._options;
15415
15310
  if (!Array.isArray(enrUrls))
15416
15311
  enrUrls = [enrUrls];
15417
- const { wantedNodeCapabilityCount } = this._options;
15418
15312
  const dns = await DnsNodeDiscovery.dnsOverHttp();
15419
- this.nextPeer = dns.getNextPeer.bind(dns, enrUrls, wantedNodeCapabilityCount);
15313
+ this.nextPeer = dns.getNextPeer.bind(dns, enrUrls);
15420
15314
  }
15421
15315
  for await (const peerEnr of this.nextPeer()) {
15422
15316
  if (!this._started) {
@@ -15433,8 +15327,8 @@ class PeerDiscoveryDns extends TypedEventEmitter {
15433
15327
  }
15434
15328
  };
15435
15329
  let isPeerChanged = false;
15436
- const isPeerExists = await this._components.peerStore.has(peerInfo.id);
15437
- if (isPeerExists) {
15330
+ const isPeerAlreadyInPeerStore = await this._components.peerStore.has(peerInfo.id);
15331
+ if (isPeerAlreadyInPeerStore) {
15438
15332
  const peer = await this._components.peerStore.get(peerInfo.id);
15439
15333
  const hasBootstrapTag = peer.tags.has(DEFAULT_BOOTSTRAP_TAG_NAME);
15440
15334
  if (!hasBootstrapTag) {
@@ -15473,8 +15367,8 @@ class PeerDiscoveryDns extends TypedEventEmitter {
15473
15367
  return DNS_DISCOVERY_TAG;
15474
15368
  }
15475
15369
  }
15476
- function wakuDnsDiscovery(enrUrls, wantedNodeCapabilityCount = DEFAULT_NODE_REQUIREMENTS) {
15477
- return (components) => new PeerDiscoveryDns(components, { enrUrls, wantedNodeCapabilityCount });
15370
+ function wakuDnsDiscovery(enrUrls) {
15371
+ return (components) => new PeerDiscoveryDns(components, { enrUrls });
15478
15372
  }
15479
15373
 
15480
15374
  /* eslint-disable import/export */
@@ -18020,6 +17914,10 @@ var SdsMessage;
18020
17914
  if (opts.lengthDelimited !== false) {
18021
17915
  w.fork();
18022
17916
  }
17917
+ if ((obj.senderId != null && obj.senderId !== '')) {
17918
+ w.uint32(10);
17919
+ w.string(obj.senderId);
17920
+ }
18023
17921
  if ((obj.messageId != null && obj.messageId !== '')) {
18024
17922
  w.uint32(18);
18025
17923
  w.string(obj.messageId);
@@ -18051,6 +17949,7 @@ var SdsMessage;
18051
17949
  }
18052
17950
  }, (reader, length, opts = {}) => {
18053
17951
  const obj = {
17952
+ senderId: '',
18054
17953
  messageId: '',
18055
17954
  channelId: '',
18056
17955
  causalHistory: []
@@ -18059,6 +17958,10 @@ var SdsMessage;
18059
17958
  while (reader.pos < end) {
18060
17959
  const tag = reader.uint32();
18061
17960
  switch (tag >>> 3) {
17961
+ case 1: {
17962
+ obj.senderId = reader.string();
17963
+ break;
17964
+ }
18062
17965
  case 2: {
18063
17966
  obj.messageId = reader.string();
18064
17967
  break;
@@ -19078,13 +18981,11 @@ class StreamManager {
19078
18981
  this.streamPool.delete(peerIdStr);
19079
18982
  await scheduledStream;
19080
18983
  }
19081
- let stream = this.getOpenStreamForCodec(peerId);
19082
- if (stream) {
19083
- this.log.info(`Found existing stream peerId=${peerIdStr} multicodec=${this.multicodec}`);
19084
- this.lockStream(peerIdStr, stream);
19085
- return stream;
18984
+ const stream = this.getOpenStreamForCodec(peerId) || (await this.createStream(peerId));
18985
+ if (!stream) {
18986
+ return;
19086
18987
  }
19087
- stream = await this.createStream(peerId);
18988
+ this.log.info(`Using stream for peerId=${peerIdStr} multicodec=${this.multicodec}`);
19088
18989
  this.lockStream(peerIdStr, stream);
19089
18990
  return stream;
19090
18991
  }
@@ -19092,7 +18993,8 @@ class StreamManager {
19092
18993
  const connections = this.libp2p.connectionManager.getConnections(peerId);
19093
18994
  const connection = selectOpenConnection(connections);
19094
18995
  if (!connection) {
19095
- throw new Error(`Failed to get a connection to the peer peerId=${peerId.toString()} multicodec=${this.multicodec}`);
18996
+ this.log.error(`Failed to get a connection to the peer peerId=${peerId.toString()} multicodec=${this.multicodec}`);
18997
+ return;
19096
18998
  }
19097
18999
  let lastError;
19098
19000
  let stream;
@@ -19108,7 +19010,8 @@ class StreamManager {
19108
19010
  }
19109
19011
  }
19110
19012
  if (!stream) {
19111
- throw new Error(`Failed to create a new stream for ${peerId.toString()} -- ` + lastError);
19013
+ this.log.error(`Failed to create a new stream for ${peerId.toString()} -- ` + lastError);
19014
+ return;
19112
19015
  }
19113
19016
  return stream;
19114
19017
  }
@@ -19153,16 +19056,20 @@ class StreamManager {
19153
19056
  const connections = this.libp2p.connectionManager.getConnections(peerId);
19154
19057
  const connection = selectOpenConnection(connections);
19155
19058
  if (!connection) {
19059
+ this.log.info(`No open connection found for peerId=${peerId.toString()} multicodec=${this.multicodec}`);
19156
19060
  return;
19157
19061
  }
19158
19062
  const stream = connection.streams.find((s) => s.protocol === this.multicodec);
19159
19063
  if (!stream) {
19064
+ this.log.info(`No open stream found for peerId=${peerId.toString()} multicodec=${this.multicodec}`);
19160
19065
  return;
19161
19066
  }
19162
19067
  const isStreamUnusable = ["done", "closed", "closing"].includes(stream.writeStatus || "");
19163
19068
  if (isStreamUnusable || this.isStreamLocked(stream)) {
19069
+ this.log.info(`Stream for peerId=${peerId.toString()} multicodec=${this.multicodec} is unusable`);
19164
19070
  return;
19165
19071
  }
19072
+ this.log.info(`Found open stream for peerId=${peerId.toString()} multicodec=${this.multicodec}`);
19166
19073
  return stream;
19167
19074
  }
19168
19075
  lockStream(peerId, stream) {
@@ -19264,12 +19171,9 @@ class WakuPeerExchange {
19264
19171
  error: ProtocolError.NO_PEER_AVAILABLE
19265
19172
  };
19266
19173
  }
19267
- let stream;
19268
- try {
19269
- stream = await this.streamManager.getStream(peerId);
19270
- }
19271
- catch (err) {
19272
- log$2.error("Failed to get stream", err);
19174
+ const stream = await this.streamManager.getStream(peerId);
19175
+ if (!stream) {
19176
+ log$2.error(`Failed to get a stream for remote peer:${peerId.toString()}`);
19273
19177
  return {
19274
19178
  peerInfos: null,
19275
19179
  error: ProtocolError.NO_STREAM_AVAILABLE