@waku/core 0.0.37-987c6cd.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.
- package/bundle/index.js +168 -66
- package/bundle/lib/message/version_0.js +1 -1
- package/bundle/{version_0-BpF0pNhc.js → version_0-BHaZD8Qu.js} +172 -26
- package/dist/.tsbuildinfo +1 -1
- package/dist/lib/connection_manager/connection_limiter.d.ts +12 -3
- package/dist/lib/connection_manager/connection_limiter.js +98 -45
- package/dist/lib/connection_manager/connection_limiter.js.map +1 -1
- package/dist/lib/connection_manager/connection_manager.d.ts +1 -4
- package/dist/lib/connection_manager/connection_manager.js +15 -7
- package/dist/lib/connection_manager/connection_manager.js.map +1 -1
- package/dist/lib/connection_manager/dialer.d.ts +7 -1
- package/dist/lib/connection_manager/dialer.js +41 -6
- package/dist/lib/connection_manager/dialer.js.map +1 -1
- package/dist/lib/connection_manager/discovery_dialer.d.ts +1 -1
- package/dist/lib/connection_manager/discovery_dialer.js.map +1 -1
- package/dist/lib/metadata/metadata.d.ts +2 -2
- package/dist/lib/metadata/metadata.js +14 -8
- package/dist/lib/metadata/metadata.js.map +1 -1
- package/package.json +1 -1
- package/src/lib/connection_manager/connection_limiter.ts +156 -65
- package/src/lib/connection_manager/connection_manager.ts +16 -12
- package/src/lib/connection_manager/dialer.ts +60 -7
- package/src/lib/connection_manager/discovery_dialer.ts +2 -4
- package/src/lib/metadata/metadata.ts +13 -12
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../../src/lib/metadata/metadata.ts"],"names":[],"mappings":"AAEA,OAAO,
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../../src/lib/metadata/metadata.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"name":"@waku/core","version":"0.0.37-
|
1
|
+
{"name":"@waku/core","version":"0.0.37-c24842a.0","description":"TypeScript implementation of the Waku v2 protocol","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./lib/message/version_0":{"types":"./dist/lib/message/version_0.d.ts","import":"./dist/lib/message/version_0.js"}},"typesVersions":{"*":{"lib/*":["dist/lib/*"],"constants/*":["dist/constants/*"]}},"type":"module","homepage":"https://github.com/waku-org/js-waku/tree/master/packages/core#readme","repository":{"type":"git","url":"https://github.com/waku-org/js-waku.git"},"bugs":{"url":"https://github.com/waku-org/js-waku/issues"},"license":"MIT OR Apache-2.0","keywords":["waku","decentralised","communication","web3","ethereum","dapps"],"scripts":{"build":"run-s build:**","build:esm":"tsc","build:bundle":"rollup --config rollup.config.js","fix":"run-s fix:*","fix:lint":"eslint src *.js --fix","check":"run-s check:*","check:tsc":"tsc -p tsconfig.dev.json","check:lint":"eslint src *.js","check:spelling":"cspell \"{README.md,src/**/*.ts}\"","test":"NODE_ENV=test run-s test:*","test:node":"NODE_ENV=test TS_NODE_PROJECT=./tsconfig.dev.json mocha","test:browser":"NODE_ENV=test karma start karma.conf.cjs","watch:build":"tsc -p tsconfig.json -w","watch:test":"mocha --watch","prepublish":"npm run build","reset-hard":"git clean -dfx -e .idea && git reset --hard && npm i && npm run build"},"engines":{"node":">=22"},"dependencies":{"@waku/enr":"0.0.31-c24842a.0","@waku/interfaces":"0.0.32-c24842a.0","@libp2p/ping":"2.0.35","@waku/proto":"0.0.12-c24842a.0","@waku/utils":"0.0.25-c24842a.0","debug":"^4.3.4","@noble/hashes":"^1.3.2","it-all":"^3.0.4","it-length-prefixed":"^9.0.4","it-pipe":"^3.0.1","uint8arraylist":"^2.4.3","uuid":"^9.0.0"},"devDependencies":{"@libp2p/peer-id":"5.1.7","@libp2p/interface":"2.10.4","@multiformats/multiaddr":"^12.0.0","@rollup/plugin-commonjs":"^25.0.7","@rollup/plugin-json":"^6.0.0","@rollup/plugin-node-resolve":"^15.2.3","@types/chai":"^4.3.11","@types/debug":"^4.1.12","@types/mocha":"^10.0.6","@types/uuid":"^9.0.8","@waku/build-utils":"*","chai":"^4.3.10","sinon":"^18.0.0","cspell":"^8.6.1","fast-check":"^3.19.0","ignore-loader":"^0.1.2","isomorphic-fetch":"^3.0.0","mocha":"^10.3.0","npm-run-all":"^4.1.5","process":"^0.11.10","rollup":"^4.12.0"},"peerDependencies":{"@multiformats/multiaddr":"^12.0.0","libp2p":"2.8.11"},"peerDependenciesMeta":{"@multiformats/multiaddr":{"optional":true},"libp2p":{"optional":true}},"files":["dist","bundle","src/**/*.ts","!**/*.spec.*","!**/*.json","CHANGELOG.md","LICENSE","README.md"]}
|
@@ -1,8 +1,10 @@
|
|
1
1
|
import { Peer, PeerId } from "@libp2p/interface";
|
2
2
|
import {
|
3
|
+
CONNECTION_LOCKED_TAG,
|
3
4
|
ConnectionManagerOptions,
|
4
5
|
IWakuEventEmitter,
|
5
6
|
Libp2p,
|
7
|
+
Libp2pEventHandler,
|
6
8
|
Tags
|
7
9
|
} from "@waku/interfaces";
|
8
10
|
import { Logger } from "@waku/utils";
|
@@ -12,7 +14,7 @@ import { NetworkMonitor } from "./network_monitor.js";
|
|
12
14
|
|
13
15
|
const log = new Logger("connection-limiter");
|
14
16
|
|
15
|
-
|
17
|
+
const DEFAULT_CONNECTION_MONITOR_INTERVAL = 5 * 1_000;
|
16
18
|
|
17
19
|
type ConnectionLimiterConstructorOptions = {
|
18
20
|
libp2p: Libp2p;
|
@@ -38,6 +40,7 @@ export class ConnectionLimiter implements IConnectionLimiter {
|
|
38
40
|
private readonly networkMonitor: NetworkMonitor;
|
39
41
|
private readonly dialer: Dialer;
|
40
42
|
|
43
|
+
private connectionMonitorInterval: NodeJS.Timeout | null = null;
|
41
44
|
private readonly options: ConnectionManagerOptions;
|
42
45
|
|
43
46
|
public constructor(options: ConnectionLimiterConstructorOptions) {
|
@@ -49,7 +52,6 @@ export class ConnectionLimiter implements IConnectionLimiter {
|
|
49
52
|
this.options = options.options;
|
50
53
|
|
51
54
|
this.onWakuConnectionEvent = this.onWakuConnectionEvent.bind(this);
|
52
|
-
this.onConnectedEvent = this.onConnectedEvent.bind(this);
|
53
55
|
this.onDisconnectedEvent = this.onDisconnectedEvent.bind(this);
|
54
56
|
}
|
55
57
|
|
@@ -57,12 +59,17 @@ export class ConnectionLimiter implements IConnectionLimiter {
|
|
57
59
|
// dial all known peers because libp2p might have emitted `peer:discovery` before initialization
|
58
60
|
void this.dialPeersFromStore();
|
59
61
|
|
60
|
-
|
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
|
+
}
|
61
71
|
|
62
|
-
this.
|
63
|
-
"peer:connect",
|
64
|
-
this.onConnectedEvent as Libp2pEventHandler<PeerId>
|
65
|
-
);
|
72
|
+
this.events.addEventListener("waku:connection", this.onWakuConnectionEvent);
|
66
73
|
|
67
74
|
/**
|
68
75
|
* NOTE: Event is not being emitted on closing nor losing a connection.
|
@@ -87,46 +94,33 @@ export class ConnectionLimiter implements IConnectionLimiter {
|
|
87
94
|
this.onWakuConnectionEvent
|
88
95
|
);
|
89
96
|
|
90
|
-
this.libp2p.removeEventListener(
|
91
|
-
"peer:connect",
|
92
|
-
this.onConnectedEvent as Libp2pEventHandler<PeerId>
|
93
|
-
);
|
94
|
-
|
95
97
|
this.libp2p.removeEventListener(
|
96
98
|
"peer:disconnect",
|
97
99
|
this.onDisconnectedEvent as Libp2pEventHandler<PeerId>
|
98
100
|
);
|
99
|
-
}
|
100
101
|
|
101
|
-
|
102
|
-
|
103
|
-
|
102
|
+
if (this.connectionMonitorInterval) {
|
103
|
+
clearInterval(this.connectionMonitorInterval);
|
104
|
+
this.connectionMonitorInterval = null;
|
104
105
|
}
|
105
106
|
}
|
106
107
|
|
107
|
-
private
|
108
|
-
|
109
|
-
|
110
|
-
const peerId = evt.detail;
|
111
|
-
|
112
|
-
const tags = await this.getTagsForPeer(peerId);
|
113
|
-
const isBootstrap = tags.includes(Tags.BOOTSTRAP);
|
114
|
-
|
115
|
-
if (!isBootstrap) {
|
116
|
-
log.info(
|
117
|
-
`Connected to peer ${peerId.toString()} is not a bootstrap peer`
|
118
|
-
);
|
108
|
+
private onWakuConnectionEvent(): void {
|
109
|
+
if (!this.options.enableAutoRecovery) {
|
110
|
+
log.info(`Auto recovery is disabled, skipping`);
|
119
111
|
return;
|
120
112
|
}
|
121
113
|
|
122
|
-
if (
|
123
|
-
|
124
|
-
`Connected to peer ${peerId.toString()} and node has more than max bootstrap connections ${this.options.maxBootstrapPeers}. Dropping connection.`
|
125
|
-
);
|
126
|
-
await this.libp2p.hangUp(peerId);
|
114
|
+
if (this.networkMonitor.isBrowserConnected()) {
|
115
|
+
void this.dialPeersFromStore();
|
127
116
|
}
|
128
117
|
}
|
129
118
|
|
119
|
+
private async maintainConnections(): Promise<void> {
|
120
|
+
await this.maintainConnectionsCount();
|
121
|
+
await this.maintainBootstrapConnections();
|
122
|
+
}
|
123
|
+
|
130
124
|
private async onDisconnectedEvent(): Promise<void> {
|
131
125
|
if (this.libp2p.getConnections().length === 0) {
|
132
126
|
log.info(`No connections, dialing peers from store`);
|
@@ -134,68 +128,165 @@ export class ConnectionLimiter implements IConnectionLimiter {
|
|
134
128
|
}
|
135
129
|
}
|
136
130
|
|
137
|
-
private async
|
138
|
-
log.info(`
|
131
|
+
private async maintainConnectionsCount(): Promise<void> {
|
132
|
+
log.info(`Maintaining connections count`);
|
139
133
|
|
140
|
-
const
|
141
|
-
|
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
|
+
}
|
142
155
|
|
143
156
|
log.info(
|
144
|
-
`
|
157
|
+
`Node has more than max connections ${this.options.maxConnections}, dropping connections`
|
145
158
|
);
|
146
159
|
|
147
|
-
const promises = allPeers
|
148
|
-
.filter((p) => !allConnections.some((c) => c.remotePeer.equals(p.id)))
|
149
|
-
.map((p) => this.dialer.dial(p.id));
|
150
|
-
|
151
160
|
try {
|
152
|
-
|
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
|
+
);
|
153
173
|
await Promise.all(promises);
|
154
|
-
|
174
|
+
|
175
|
+
log.info(`Dropped ${connectionsToDrop.length} connections`);
|
155
176
|
} catch (error) {
|
156
|
-
log.error(`Unexpected error while
|
177
|
+
log.error(`Unexpected error while maintaining connections`, error);
|
157
178
|
}
|
158
179
|
}
|
159
180
|
|
160
|
-
private async
|
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
|
+
|
161
190
|
try {
|
162
|
-
const
|
163
|
-
this.libp2p
|
164
|
-
.getConnections()
|
165
|
-
.map((conn) => conn.remotePeer)
|
166
|
-
.map((id) => this.getPeer(id))
|
167
|
-
);
|
191
|
+
const peersToDrop = bootstrapPeers.slice(this.options.maxBootstrapPeers);
|
168
192
|
|
169
|
-
|
170
|
-
|
193
|
+
log.info(
|
194
|
+
`Dropping ${peersToDrop.length} bootstrap connections because node has more than max bootstrap connections ${this.options.maxBootstrapPeers}`
|
171
195
|
);
|
172
196
|
|
173
|
-
|
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`);
|
174
201
|
} catch (error) {
|
175
202
|
log.error(
|
176
|
-
`Unexpected error while
|
203
|
+
`Unexpected error while maintaining bootstrap connections`,
|
177
204
|
error
|
178
205
|
);
|
179
|
-
return false;
|
180
206
|
}
|
181
207
|
}
|
182
208
|
|
183
|
-
private async
|
209
|
+
private async dialPeersFromStore(): Promise<void> {
|
210
|
+
log.info(`Dialing peers from store`);
|
211
|
+
|
184
212
|
try {
|
185
|
-
|
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`);
|
186
225
|
} catch (error) {
|
187
|
-
log.error(`
|
188
|
-
return null;
|
226
|
+
log.error(`Unexpected error while dialing peer store peers`, error);
|
189
227
|
}
|
190
228
|
}
|
191
229
|
|
192
|
-
|
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> {
|
193
285
|
try {
|
194
|
-
|
195
|
-
return Array.from(peer.tags.keys());
|
286
|
+
return await this.libp2p.peerStore.get(peerId);
|
196
287
|
} catch (error) {
|
197
288
|
log.error(`Failed to get peer ${peerId}, error: ${error}`);
|
198
|
-
return
|
289
|
+
return null;
|
199
290
|
}
|
200
291
|
}
|
201
292
|
}
|
@@ -5,8 +5,7 @@ import {
|
|
5
5
|
IConnectionManager,
|
6
6
|
IRelay,
|
7
7
|
IWakuEventEmitter,
|
8
|
-
NetworkConfig
|
9
|
-
PubsubTopic
|
8
|
+
NetworkConfig
|
10
9
|
} from "@waku/interfaces";
|
11
10
|
import { Libp2p } from "@waku/interfaces";
|
12
11
|
import { Logger } from "@waku/utils";
|
@@ -21,22 +20,24 @@ import { getPeerPing, mapToPeerId, mapToPeerIdOrMultiaddr } from "./utils.js";
|
|
21
20
|
|
22
21
|
const log = new Logger("connection-manager");
|
23
22
|
|
24
|
-
const DEFAULT_MAX_BOOTSTRAP_PEERS_ALLOWED =
|
23
|
+
const DEFAULT_MAX_BOOTSTRAP_PEERS_ALLOWED = 3;
|
25
24
|
const DEFAULT_PING_KEEP_ALIVE_SEC = 5 * 60;
|
26
25
|
const DEFAULT_RELAY_KEEP_ALIVE_SEC = 5 * 60;
|
26
|
+
const DEFAULT_ENABLE_AUTO_RECOVERY = true;
|
27
|
+
const DEFAULT_MAX_CONNECTIONS = 10;
|
28
|
+
const DEFAULT_MAX_DIALING_PEERS = 3;
|
29
|
+
const DEFAULT_FAILED_DIAL_COOLDOWN_SEC = 60;
|
30
|
+
const DEFAULT_DIAL_COOLDOWN_SEC = 10;
|
27
31
|
|
28
32
|
type ConnectionManagerConstructorOptions = {
|
29
33
|
libp2p: Libp2p;
|
30
34
|
events: IWakuEventEmitter;
|
31
|
-
pubsubTopics: PubsubTopic[];
|
32
35
|
networkConfig: NetworkConfig;
|
33
36
|
relay?: IRelay;
|
34
37
|
config?: Partial<ConnectionManagerOptions>;
|
35
38
|
};
|
36
39
|
|
37
40
|
export class ConnectionManager implements IConnectionManager {
|
38
|
-
private readonly pubsubTopics: PubsubTopic[];
|
39
|
-
|
40
41
|
private readonly keepAliveManager: KeepAliveManager;
|
41
42
|
private readonly discoveryDialer: DiscoveryDialer;
|
42
43
|
private readonly dialer: Dialer;
|
@@ -49,12 +50,16 @@ export class ConnectionManager implements IConnectionManager {
|
|
49
50
|
|
50
51
|
public constructor(options: ConnectionManagerConstructorOptions) {
|
51
52
|
this.libp2p = options.libp2p;
|
52
|
-
this.pubsubTopics = options.pubsubTopics;
|
53
53
|
|
54
54
|
this.options = {
|
55
55
|
maxBootstrapPeers: DEFAULT_MAX_BOOTSTRAP_PEERS_ALLOWED,
|
56
|
+
maxConnections: DEFAULT_MAX_CONNECTIONS,
|
56
57
|
pingKeepAlive: DEFAULT_PING_KEEP_ALIVE_SEC,
|
57
58
|
relayKeepAlive: DEFAULT_RELAY_KEEP_ALIVE_SEC,
|
59
|
+
enableAutoRecovery: DEFAULT_ENABLE_AUTO_RECOVERY,
|
60
|
+
maxDialingPeers: DEFAULT_MAX_DIALING_PEERS,
|
61
|
+
failedDialCooldown: DEFAULT_FAILED_DIAL_COOLDOWN_SEC,
|
62
|
+
dialCooldown: DEFAULT_DIAL_COOLDOWN_SEC,
|
58
63
|
...options.config
|
59
64
|
};
|
60
65
|
|
@@ -74,7 +79,8 @@ export class ConnectionManager implements IConnectionManager {
|
|
74
79
|
|
75
80
|
this.dialer = new Dialer({
|
76
81
|
libp2p: options.libp2p,
|
77
|
-
shardReader: this.shardReader
|
82
|
+
shardReader: this.shardReader,
|
83
|
+
options: this.options
|
78
84
|
});
|
79
85
|
|
80
86
|
this.discoveryDialer = new DiscoveryDialer({
|
@@ -97,6 +103,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
97
103
|
}
|
98
104
|
|
99
105
|
public start(): void {
|
106
|
+
this.dialer.start();
|
100
107
|
this.networkMonitor.start();
|
101
108
|
this.discoveryDialer.start();
|
102
109
|
this.keepAliveManager.start();
|
@@ -104,6 +111,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
104
111
|
}
|
105
112
|
|
106
113
|
public stop(): void {
|
114
|
+
this.dialer.stop();
|
107
115
|
this.networkMonitor.stop();
|
108
116
|
this.discoveryDialer.stop();
|
109
117
|
this.keepAliveManager.stop();
|
@@ -176,10 +184,6 @@ export class ConnectionManager implements IConnectionManager {
|
|
176
184
|
return result;
|
177
185
|
}
|
178
186
|
|
179
|
-
public isTopicConfigured(pubsubTopic: PubsubTopic): boolean {
|
180
|
-
return this.pubsubTopics.includes(pubsubTopic);
|
181
|
-
}
|
182
|
-
|
183
187
|
public async hasShardInfo(peerId: PeerId): Promise<boolean> {
|
184
188
|
return this.shardReader.hasShardInfo(peerId);
|
185
189
|
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import type { PeerId } from "@libp2p/interface";
|
2
|
-
import { Libp2p } from "@waku/interfaces";
|
2
|
+
import { ConnectionManagerOptions, Libp2p } from "@waku/interfaces";
|
3
3
|
import { Logger } from "@waku/utils";
|
4
4
|
|
5
5
|
import { ShardReader } from "./shard_reader.js";
|
@@ -9,6 +9,7 @@ const log = new Logger("dialer");
|
|
9
9
|
type DialerConstructorOptions = {
|
10
10
|
libp2p: Libp2p;
|
11
11
|
shardReader: ShardReader;
|
12
|
+
options: ConnectionManagerOptions;
|
12
13
|
};
|
13
14
|
|
14
15
|
interface IDialer {
|
@@ -20,18 +21,25 @@ interface IDialer {
|
|
20
21
|
export class Dialer implements IDialer {
|
21
22
|
private readonly libp2p: Libp2p;
|
22
23
|
private readonly shardReader: ShardReader;
|
24
|
+
private readonly options: ConnectionManagerOptions;
|
23
25
|
|
24
26
|
private dialingQueue: PeerId[] = [];
|
25
27
|
private dialHistory: Map<string, number> = new Map();
|
28
|
+
private failedDials: Map<string, number> = new Map();
|
26
29
|
private dialingInterval: NodeJS.Timeout | null = null;
|
30
|
+
|
27
31
|
private isProcessing = false;
|
32
|
+
private isImmediateDialing = false;
|
28
33
|
|
29
34
|
public constructor(options: DialerConstructorOptions) {
|
30
35
|
this.libp2p = options.libp2p;
|
31
36
|
this.shardReader = options.shardReader;
|
37
|
+
this.options = options.options;
|
32
38
|
}
|
33
39
|
|
34
40
|
public start(): void {
|
41
|
+
log.info("Starting dialer");
|
42
|
+
|
35
43
|
if (!this.dialingInterval) {
|
36
44
|
this.dialingInterval = setInterval(() => {
|
37
45
|
void this.processQueue();
|
@@ -39,15 +47,19 @@ export class Dialer implements IDialer {
|
|
39
47
|
}
|
40
48
|
|
41
49
|
this.dialHistory.clear();
|
50
|
+
this.failedDials.clear();
|
42
51
|
}
|
43
52
|
|
44
53
|
public stop(): void {
|
54
|
+
log.info("Stopping dialer");
|
55
|
+
|
45
56
|
if (this.dialingInterval) {
|
46
57
|
clearInterval(this.dialingInterval);
|
47
58
|
this.dialingInterval = null;
|
48
59
|
}
|
49
60
|
|
50
61
|
this.dialHistory.clear();
|
62
|
+
this.failedDials.clear();
|
51
63
|
}
|
52
64
|
|
53
65
|
public async dial(peerId: PeerId): Promise<void> {
|
@@ -58,11 +70,17 @@ export class Dialer implements IDialer {
|
|
58
70
|
return;
|
59
71
|
}
|
60
72
|
|
73
|
+
const isEmptyQueue = this.dialingQueue.length === 0;
|
74
|
+
const isNotDialing = !this.isProcessing && !this.isImmediateDialing;
|
75
|
+
|
61
76
|
// If queue is empty and we're not currently processing, dial immediately
|
62
|
-
if (
|
77
|
+
if (isEmptyQueue && isNotDialing) {
|
78
|
+
this.isImmediateDialing = true;
|
79
|
+
log.info("Dialed peer immediately");
|
63
80
|
await this.dialPeer(peerId);
|
81
|
+
this.isImmediateDialing = false;
|
82
|
+
log.info("Released immediate dial lock");
|
64
83
|
} else {
|
65
|
-
// Add to queue
|
66
84
|
this.dialingQueue.push(peerId);
|
67
85
|
log.info(
|
68
86
|
`Added peer to dialing queue, queue size: ${this.dialingQueue.length}`
|
@@ -71,12 +89,17 @@ export class Dialer implements IDialer {
|
|
71
89
|
}
|
72
90
|
|
73
91
|
private async processQueue(): Promise<void> {
|
74
|
-
if (this.dialingQueue.length === 0 || this.isProcessing)
|
92
|
+
if (this.dialingQueue.length === 0 || this.isProcessing) {
|
93
|
+
return;
|
94
|
+
}
|
75
95
|
|
76
96
|
this.isProcessing = true;
|
77
97
|
|
78
98
|
try {
|
79
|
-
const peersToDial = this.dialingQueue.slice(
|
99
|
+
const peersToDial = this.dialingQueue.slice(
|
100
|
+
0,
|
101
|
+
this.options.maxDialingPeers
|
102
|
+
);
|
80
103
|
this.dialingQueue = this.dialingQueue.slice(peersToDial.length);
|
81
104
|
|
82
105
|
log.info(
|
@@ -95,10 +118,12 @@ export class Dialer implements IDialer {
|
|
95
118
|
|
96
119
|
await this.libp2p.dial(peerId);
|
97
120
|
this.dialHistory.set(peerId.toString(), Date.now());
|
121
|
+
this.failedDials.delete(peerId.toString());
|
98
122
|
|
99
123
|
log.info(`Successfully dialed peer from queue: ${peerId}`);
|
100
124
|
} catch (error) {
|
101
125
|
log.error(`Error dialing peer ${peerId}`, error);
|
126
|
+
this.failedDials.set(peerId.toString(), Date.now());
|
102
127
|
}
|
103
128
|
}
|
104
129
|
|
@@ -109,14 +134,18 @@ export class Dialer implements IDialer {
|
|
109
134
|
return true;
|
110
135
|
}
|
111
136
|
|
112
|
-
|
113
|
-
if (lastDialed && Date.now() - lastDialed < 10_000) {
|
137
|
+
if (this.isRecentlyDialed(peerId)) {
|
114
138
|
log.info(
|
115
139
|
`Skipping peer ${peerId} - already dialed in the last 10 seconds`
|
116
140
|
);
|
117
141
|
return true;
|
118
142
|
}
|
119
143
|
|
144
|
+
if (this.isRecentlyFailed(peerId)) {
|
145
|
+
log.info(`Skipping peer ${peerId} - recently failed to dial`);
|
146
|
+
return true;
|
147
|
+
}
|
148
|
+
|
120
149
|
try {
|
121
150
|
const hasShardInfo = await this.shardReader.hasShardInfo(peerId);
|
122
151
|
if (!hasShardInfo) {
|
@@ -136,4 +165,28 @@ export class Dialer implements IDialer {
|
|
136
165
|
return true; // Skip peer when there's an error
|
137
166
|
}
|
138
167
|
}
|
168
|
+
|
169
|
+
private isRecentlyDialed(peerId: PeerId): boolean {
|
170
|
+
const lastDialed = this.dialHistory.get(peerId.toString());
|
171
|
+
if (
|
172
|
+
lastDialed &&
|
173
|
+
Date.now() - lastDialed < this.options.dialCooldown * 1000
|
174
|
+
) {
|
175
|
+
return true;
|
176
|
+
}
|
177
|
+
|
178
|
+
return false;
|
179
|
+
}
|
180
|
+
|
181
|
+
private isRecentlyFailed(peerId: PeerId): boolean {
|
182
|
+
const lastFailed = this.failedDials.get(peerId.toString());
|
183
|
+
if (
|
184
|
+
lastFailed &&
|
185
|
+
Date.now() - lastFailed < this.options.failedDialCooldown * 1000
|
186
|
+
) {
|
187
|
+
return true;
|
188
|
+
}
|
189
|
+
|
190
|
+
return false;
|
191
|
+
}
|
139
192
|
}
|
@@ -1,12 +1,10 @@
|
|
1
|
-
import { Peer, PeerId, PeerInfo } from "@libp2p/interface";
|
1
|
+
import { Libp2p, Peer, PeerId, PeerInfo } from "@libp2p/interface";
|
2
2
|
import { Multiaddr } from "@multiformats/multiaddr";
|
3
|
+
import { Libp2pEventHandler } from "@waku/interfaces";
|
3
4
|
import { Logger } from "@waku/utils";
|
4
|
-
import { Libp2p } from "libp2p";
|
5
5
|
|
6
6
|
import { Dialer } from "./dialer.js";
|
7
7
|
|
8
|
-
type Libp2pEventHandler<T> = (e: CustomEvent<T>) => void;
|
9
|
-
|
10
8
|
type DiscoveryDialerConstructorOptions = {
|
11
9
|
libp2p: Libp2p;
|
12
10
|
dialer: Dialer;
|