@waku/core 0.0.37-7a9850d.0 → 0.0.37-c24842a.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 (49) hide show
  1. package/bundle/index.js +4852 -904
  2. package/bundle/lib/message/version_0.js +1 -1
  3. package/bundle/{version_0-9DPFjcJG.js → version_0-BHaZD8Qu.js} +485 -60
  4. package/dist/.tsbuildinfo +1 -1
  5. package/dist/lib/connection_manager/connection_limiter.d.ts +46 -0
  6. package/dist/lib/connection_manager/connection_limiter.js +177 -0
  7. package/dist/lib/connection_manager/connection_limiter.js.map +1 -0
  8. package/dist/lib/connection_manager/connection_manager.d.ts +18 -106
  9. package/dist/lib/connection_manager/connection_manager.js +95 -512
  10. package/dist/lib/connection_manager/connection_manager.js.map +1 -1
  11. package/dist/lib/connection_manager/dialer.d.ts +34 -0
  12. package/dist/lib/connection_manager/dialer.js +135 -0
  13. package/dist/lib/connection_manager/dialer.js.map +1 -0
  14. package/dist/lib/connection_manager/discovery_dialer.d.ts +26 -0
  15. package/dist/lib/connection_manager/discovery_dialer.js +68 -0
  16. package/dist/lib/connection_manager/discovery_dialer.js.map +1 -0
  17. package/dist/lib/connection_manager/keep_alive_manager.d.ts +17 -7
  18. package/dist/lib/connection_manager/keep_alive_manager.js +110 -74
  19. package/dist/lib/connection_manager/keep_alive_manager.js.map +1 -1
  20. package/dist/lib/connection_manager/network_monitor.d.ts +36 -0
  21. package/dist/lib/connection_manager/network_monitor.js +81 -0
  22. package/dist/lib/connection_manager/network_monitor.js.map +1 -0
  23. package/dist/lib/connection_manager/shard_reader.d.ts +28 -0
  24. package/dist/lib/connection_manager/shard_reader.js +70 -0
  25. package/dist/lib/connection_manager/shard_reader.js.map +1 -0
  26. package/dist/lib/connection_manager/utils.d.ts +16 -1
  27. package/dist/lib/connection_manager/utils.js +23 -0
  28. package/dist/lib/connection_manager/utils.js.map +1 -1
  29. package/dist/lib/filter/filter.d.ts +2 -3
  30. package/dist/lib/filter/filter.js +5 -25
  31. package/dist/lib/filter/filter.js.map +1 -1
  32. package/dist/lib/light_push/light_push.d.ts +2 -3
  33. package/dist/lib/light_push/light_push.js +1 -3
  34. package/dist/lib/light_push/light_push.js.map +1 -1
  35. package/dist/lib/metadata/metadata.d.ts +2 -2
  36. package/dist/lib/metadata/metadata.js +14 -8
  37. package/dist/lib/metadata/metadata.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/lib/connection_manager/connection_limiter.ts +292 -0
  40. package/src/lib/connection_manager/connection_manager.ts +112 -673
  41. package/src/lib/connection_manager/dialer.ts +192 -0
  42. package/src/lib/connection_manager/discovery_dialer.ts +104 -0
  43. package/src/lib/connection_manager/keep_alive_manager.ts +154 -87
  44. package/src/lib/connection_manager/network_monitor.ts +112 -0
  45. package/src/lib/connection_manager/shard_reader.ts +134 -0
  46. package/src/lib/connection_manager/utils.ts +27 -1
  47. package/src/lib/filter/filter.ts +3 -28
  48. package/src/lib/light_push/light_push.ts +1 -5
  49. package/src/lib/metadata/metadata.ts +13 -12
@@ -0,0 +1,292 @@
1
+ import { Peer, PeerId } from "@libp2p/interface";
2
+ import {
3
+ CONNECTION_LOCKED_TAG,
4
+ ConnectionManagerOptions,
5
+ IWakuEventEmitter,
6
+ Libp2p,
7
+ Libp2pEventHandler,
8
+ Tags
9
+ } from "@waku/interfaces";
10
+ import { Logger } from "@waku/utils";
11
+
12
+ import { Dialer } from "./dialer.js";
13
+ import { NetworkMonitor } from "./network_monitor.js";
14
+
15
+ const log = new Logger("connection-limiter");
16
+
17
+ const DEFAULT_CONNECTION_MONITOR_INTERVAL = 5 * 1_000;
18
+
19
+ type ConnectionLimiterConstructorOptions = {
20
+ libp2p: Libp2p;
21
+ events: IWakuEventEmitter;
22
+ dialer: Dialer;
23
+ networkMonitor: NetworkMonitor;
24
+ options: ConnectionManagerOptions;
25
+ };
26
+
27
+ interface IConnectionLimiter {
28
+ start(): void;
29
+ stop(): void;
30
+ }
31
+
32
+ /**
33
+ * This class is responsible for limiting the number of connections to peers.
34
+ * It also dials all known peers because libp2p might have emitted `peer:discovery` before initialization
35
+ * and listen to `peer:connect` and `peer:disconnect` events to manage connections.
36
+ */
37
+ export class ConnectionLimiter implements IConnectionLimiter {
38
+ private readonly libp2p: Libp2p;
39
+ private readonly events: IWakuEventEmitter;
40
+ private readonly networkMonitor: NetworkMonitor;
41
+ private readonly dialer: Dialer;
42
+
43
+ private connectionMonitorInterval: NodeJS.Timeout | null = null;
44
+ private readonly options: ConnectionManagerOptions;
45
+
46
+ public constructor(options: ConnectionLimiterConstructorOptions) {
47
+ this.libp2p = options.libp2p;
48
+ this.events = options.events;
49
+ this.networkMonitor = options.networkMonitor;
50
+ this.dialer = options.dialer;
51
+
52
+ this.options = options.options;
53
+
54
+ this.onWakuConnectionEvent = this.onWakuConnectionEvent.bind(this);
55
+ this.onDisconnectedEvent = this.onDisconnectedEvent.bind(this);
56
+ }
57
+
58
+ public start(): void {
59
+ // dial all known peers because libp2p might have emitted `peer:discovery` before initialization
60
+ void this.dialPeersFromStore();
61
+
62
+ if (
63
+ this.options.enableAutoRecovery &&
64
+ this.connectionMonitorInterval === null
65
+ ) {
66
+ this.connectionMonitorInterval = setInterval(
67
+ () => void this.maintainConnections(),
68
+ DEFAULT_CONNECTION_MONITOR_INTERVAL
69
+ );
70
+ }
71
+
72
+ this.events.addEventListener("waku:connection", this.onWakuConnectionEvent);
73
+
74
+ /**
75
+ * NOTE: Event is not being emitted on closing nor losing a connection.
76
+ * @see https://github.com/libp2p/js-libp2p/issues/939
77
+ * @see https://github.com/status-im/js-waku/issues/252
78
+ *
79
+ * >This event will be triggered anytime we are disconnected from another peer,
80
+ * >regardless of the circumstances of that disconnection.
81
+ * >If we happen to have multiple connections to a peer,
82
+ * >this event will **only** be triggered when the last connection is closed.
83
+ * @see https://github.com/libp2p/js-libp2p/blob/bad9e8c0ff58d60a78314077720c82ae331cc55b/doc/API.md?plain=1#L2100
84
+ */
85
+ this.libp2p.addEventListener(
86
+ "peer:disconnect",
87
+ this.onDisconnectedEvent as Libp2pEventHandler<PeerId>
88
+ );
89
+ }
90
+
91
+ public stop(): void {
92
+ this.events.removeEventListener(
93
+ "waku:connection",
94
+ this.onWakuConnectionEvent
95
+ );
96
+
97
+ this.libp2p.removeEventListener(
98
+ "peer:disconnect",
99
+ this.onDisconnectedEvent as Libp2pEventHandler<PeerId>
100
+ );
101
+
102
+ if (this.connectionMonitorInterval) {
103
+ clearInterval(this.connectionMonitorInterval);
104
+ this.connectionMonitorInterval = null;
105
+ }
106
+ }
107
+
108
+ private onWakuConnectionEvent(): void {
109
+ if (!this.options.enableAutoRecovery) {
110
+ log.info(`Auto recovery is disabled, skipping`);
111
+ return;
112
+ }
113
+
114
+ if (this.networkMonitor.isBrowserConnected()) {
115
+ void this.dialPeersFromStore();
116
+ }
117
+ }
118
+
119
+ private async maintainConnections(): Promise<void> {
120
+ await this.maintainConnectionsCount();
121
+ await this.maintainBootstrapConnections();
122
+ }
123
+
124
+ private async onDisconnectedEvent(): Promise<void> {
125
+ if (this.libp2p.getConnections().length === 0) {
126
+ log.info(`No connections, dialing peers from store`);
127
+ await this.dialPeersFromStore();
128
+ }
129
+ }
130
+
131
+ private async maintainConnectionsCount(): Promise<void> {
132
+ log.info(`Maintaining connections count`);
133
+
134
+ const connections = this.libp2p.getConnections();
135
+
136
+ if (connections.length <= this.options.maxConnections) {
137
+ log.info(
138
+ `Node has less than max connections ${this.options.maxConnections}, trying to dial more peers`
139
+ );
140
+
141
+ const peers = await this.getPrioritizedPeers();
142
+
143
+ if (peers.length === 0) {
144
+ log.info(`No peers to dial, node is utilizing all known peers`);
145
+ return;
146
+ }
147
+
148
+ const promises = peers
149
+ .slice(0, this.options.maxConnections - connections.length)
150
+ .map((p) => this.dialer.dial(p.id));
151
+ await Promise.all(promises);
152
+
153
+ return;
154
+ }
155
+
156
+ log.info(
157
+ `Node has more than max connections ${this.options.maxConnections}, dropping connections`
158
+ );
159
+
160
+ try {
161
+ const connectionsToDrop = connections
162
+ .filter((c) => !c.tags.includes(CONNECTION_LOCKED_TAG))
163
+ .slice(this.options.maxConnections);
164
+
165
+ if (connectionsToDrop.length === 0) {
166
+ log.info(`No connections to drop, skipping`);
167
+ return;
168
+ }
169
+
170
+ const promises = connectionsToDrop.map((c) =>
171
+ this.libp2p.hangUp(c.remotePeer)
172
+ );
173
+ await Promise.all(promises);
174
+
175
+ log.info(`Dropped ${connectionsToDrop.length} connections`);
176
+ } catch (error) {
177
+ log.error(`Unexpected error while maintaining connections`, error);
178
+ }
179
+ }
180
+
181
+ private async maintainBootstrapConnections(): Promise<void> {
182
+ log.info(`Maintaining bootstrap connections`);
183
+
184
+ const bootstrapPeers = await this.getBootstrapPeers();
185
+
186
+ if (bootstrapPeers.length <= this.options.maxBootstrapPeers) {
187
+ return;
188
+ }
189
+
190
+ try {
191
+ const peersToDrop = bootstrapPeers.slice(this.options.maxBootstrapPeers);
192
+
193
+ log.info(
194
+ `Dropping ${peersToDrop.length} bootstrap connections because node has more than max bootstrap connections ${this.options.maxBootstrapPeers}`
195
+ );
196
+
197
+ const promises = peersToDrop.map((p) => this.libp2p.hangUp(p.id));
198
+ await Promise.all(promises);
199
+
200
+ log.info(`Dropped ${peersToDrop.length} bootstrap connections`);
201
+ } catch (error) {
202
+ log.error(
203
+ `Unexpected error while maintaining bootstrap connections`,
204
+ error
205
+ );
206
+ }
207
+ }
208
+
209
+ private async dialPeersFromStore(): Promise<void> {
210
+ log.info(`Dialing peers from store`);
211
+
212
+ try {
213
+ const peers = await this.getPrioritizedPeers();
214
+
215
+ if (peers.length === 0) {
216
+ log.info(`No peers to dial, skipping`);
217
+ return;
218
+ }
219
+
220
+ const promises = peers.map((p) => this.dialer.dial(p.id));
221
+
222
+ log.info(`Dialing ${peers.length} peers from store`);
223
+ await Promise.all(promises);
224
+ log.info(`Dialed ${promises.length} peers from store`);
225
+ } catch (error) {
226
+ log.error(`Unexpected error while dialing peer store peers`, error);
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Returns a list of peers ordered by priority:
232
+ * - bootstrap peers
233
+ * - peers from peer exchange
234
+ * - peers from local store (last because we are not sure that locally stored information is up to date)
235
+ */
236
+ private async getPrioritizedPeers(): Promise<Peer[]> {
237
+ const allPeers = await this.libp2p.peerStore.all();
238
+ const allConnections = this.libp2p.getConnections();
239
+
240
+ log.info(
241
+ `Found ${allPeers.length} peers in store, and found ${allConnections.length} connections`
242
+ );
243
+
244
+ const notConnectedPeers = allPeers.filter(
245
+ (p) =>
246
+ !allConnections.some((c) => c.remotePeer.equals(p.id)) &&
247
+ p.addresses.some(
248
+ (a) =>
249
+ a.multiaddr.toString().includes("wss") ||
250
+ a.multiaddr.toString().includes("ws")
251
+ )
252
+ );
253
+
254
+ const bootstrapPeers = notConnectedPeers.filter((p) =>
255
+ p.tags.has(Tags.BOOTSTRAP)
256
+ );
257
+
258
+ const peerExchangePeers = notConnectedPeers.filter((p) =>
259
+ p.tags.has(Tags.PEER_EXCHANGE)
260
+ );
261
+
262
+ const localStorePeers = notConnectedPeers.filter((p) =>
263
+ p.tags.has(Tags.LOCAL)
264
+ );
265
+
266
+ return [...bootstrapPeers, ...peerExchangePeers, ...localStorePeers];
267
+ }
268
+
269
+ private async getBootstrapPeers(): Promise<Peer[]> {
270
+ const peers = await Promise.all(
271
+ this.libp2p
272
+ .getConnections()
273
+ .map((conn) => conn.remotePeer)
274
+ .map((id) => this.getPeer(id))
275
+ );
276
+
277
+ const bootstrapPeers = peers.filter(
278
+ (peer) => peer && peer.tags.has(Tags.BOOTSTRAP)
279
+ ) as Peer[];
280
+
281
+ return bootstrapPeers;
282
+ }
283
+
284
+ private async getPeer(peerId: PeerId): Promise<Peer | null> {
285
+ try {
286
+ return await this.libp2p.peerStore.get(peerId);
287
+ } catch (error) {
288
+ log.error(`Failed to get peer ${peerId}, error: ${error}`);
289
+ return null;
290
+ }
291
+ }
292
+ }