@waku/core 0.0.33 → 0.0.34-00f2e75.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/base_protocol-Bp5a9PNG.js +152 -0
- package/bundle/{index-BIW3qNYx.js → index-G1eRBjeI.js} +118 -204
- package/bundle/index.js +1895 -179
- package/bundle/lib/base_protocol.js +2 -2
- package/bundle/lib/message/version_0.js +2 -2
- package/bundle/{version_0-CdmZMfkQ.js → version_0-DJZG2fB2.js} +283 -45
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.d.ts +1 -3
- package/dist/index.js +1 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/base_protocol.d.ts +3 -23
- package/dist/lib/base_protocol.js +3 -47
- package/dist/lib/base_protocol.js.map +1 -1
- package/dist/lib/connection_manager/connection_manager.d.ts +118 -0
- package/dist/lib/{connection_manager.js → connection_manager/connection_manager.js} +136 -36
- package/dist/lib/connection_manager/connection_manager.js.map +1 -0
- package/dist/lib/connection_manager/index.d.ts +1 -0
- package/dist/lib/connection_manager/index.js +2 -0
- package/dist/lib/connection_manager/index.js.map +1 -0
- package/dist/lib/{keep_alive_manager.d.ts → connection_manager/keep_alive_manager.d.ts} +4 -2
- package/dist/lib/{keep_alive_manager.js → connection_manager/keep_alive_manager.js} +2 -2
- package/dist/lib/connection_manager/keep_alive_manager.js.map +1 -0
- package/dist/lib/connection_manager/utils.d.ts +7 -0
- package/dist/lib/connection_manager/utils.js +22 -0
- package/dist/lib/connection_manager/utils.js.map +1 -0
- package/dist/lib/filter/filter.d.ts +18 -0
- package/dist/lib/filter/filter.js +209 -0
- package/dist/lib/filter/filter.js.map +1 -0
- package/dist/lib/filter/index.d.ts +1 -18
- package/dist/lib/filter/index.js +1 -208
- package/dist/lib/filter/index.js.map +1 -1
- package/dist/lib/light_push/index.d.ts +1 -15
- package/dist/lib/light_push/index.js +1 -143
- package/dist/lib/light_push/index.js.map +1 -1
- package/dist/lib/light_push/light_push.d.ts +15 -0
- package/dist/lib/light_push/light_push.js +144 -0
- package/dist/lib/light_push/light_push.js.map +1 -0
- package/dist/lib/light_push/utils.d.ts +0 -2
- package/dist/lib/light_push/utils.js +9 -17
- package/dist/lib/light_push/utils.js.map +1 -1
- package/dist/lib/metadata/index.d.ts +1 -3
- package/dist/lib/metadata/index.js +1 -118
- package/dist/lib/metadata/index.js.map +1 -1
- package/dist/lib/metadata/metadata.d.ts +3 -0
- package/dist/lib/metadata/metadata.js +119 -0
- package/dist/lib/metadata/metadata.js.map +1 -0
- package/dist/lib/store/index.d.ts +1 -9
- package/dist/lib/store/index.js +1 -82
- package/dist/lib/store/index.js.map +1 -1
- package/dist/lib/store/store.d.ts +9 -0
- package/dist/lib/store/store.js +83 -0
- package/dist/lib/store/store.js.map +1 -0
- package/dist/lib/stream_manager/stream_manager.d.ts +2 -2
- package/dist/lib/stream_manager/stream_manager.js +16 -17
- package/dist/lib/stream_manager/stream_manager.js.map +1 -1
- package/package.json +1 -126
- package/src/index.ts +1 -4
- package/src/lib/base_protocol.ts +3 -76
- package/src/lib/{connection_manager.ts → connection_manager/connection_manager.ts} +168 -63
- package/src/lib/connection_manager/index.ts +1 -0
- package/src/lib/{keep_alive_manager.ts → connection_manager/keep_alive_manager.ts} +7 -3
- package/src/lib/connection_manager/utils.ts +25 -0
- package/src/lib/filter/filter.ts +315 -0
- package/src/lib/filter/index.ts +1 -315
- package/src/lib/light_push/index.ts +1 -188
- package/src/lib/light_push/light_push.ts +188 -0
- package/src/lib/light_push/utils.ts +13 -21
- package/src/lib/metadata/index.ts +1 -182
- package/src/lib/metadata/metadata.ts +182 -0
- package/src/lib/store/index.ts +1 -136
- package/src/lib/store/store.ts +136 -0
- package/src/lib/stream_manager/stream_manager.ts +16 -18
- package/bundle/base_protocol-Dzv-QHPR.js +0 -275
- package/dist/lib/connection_manager.d.ts +0 -62
- package/dist/lib/connection_manager.js.map +0 -1
- package/dist/lib/filterPeers.d.ts +0 -13
- package/dist/lib/filterPeers.js +0 -38
- package/dist/lib/filterPeers.js.map +0 -1
- package/dist/lib/health_manager.d.ts +0 -14
- package/dist/lib/health_manager.js +0 -70
- package/dist/lib/health_manager.js.map +0 -1
- package/dist/lib/keep_alive_manager.js.map +0 -1
- package/src/lib/filterPeers.ts +0 -51
- package/src/lib/health_manager.ts +0 -90
@@ -1,5 +1,14 @@
|
|
1
|
-
import
|
2
|
-
|
1
|
+
import {
|
2
|
+
type Connection,
|
3
|
+
isPeerId,
|
4
|
+
type Peer,
|
5
|
+
type PeerId,
|
6
|
+
type PeerInfo,
|
7
|
+
type PeerStore,
|
8
|
+
type Stream,
|
9
|
+
TypedEventEmitter
|
10
|
+
} from "@libp2p/interface";
|
11
|
+
import { Multiaddr, multiaddr, MultiaddrInput } from "@multiformats/multiaddr";
|
3
12
|
import {
|
4
13
|
ConnectionManagerOptions,
|
5
14
|
DiscoveryTrigger,
|
@@ -10,7 +19,6 @@ import {
|
|
10
19
|
IConnectionStateEvents,
|
11
20
|
IPeersByDiscoveryEvents,
|
12
21
|
IRelay,
|
13
|
-
KeepAliveOptions,
|
14
22
|
PeersByDiscoveryResult,
|
15
23
|
PubsubTopic,
|
16
24
|
ShardInfo
|
@@ -20,18 +28,31 @@ import { decodeRelayShard, shardInfoToPubsubTopics } from "@waku/utils";
|
|
20
28
|
import { Logger } from "@waku/utils";
|
21
29
|
|
22
30
|
import { KeepAliveManager } from "./keep_alive_manager.js";
|
31
|
+
import { getPeerPing } from "./utils.js";
|
23
32
|
|
24
33
|
const log = new Logger("connection-manager");
|
25
34
|
|
26
|
-
|
27
|
-
|
28
|
-
|
35
|
+
const DEFAULT_MAX_BOOTSTRAP_PEERS_ALLOWED = 1;
|
36
|
+
const DEFAULT_MAX_DIAL_ATTEMPTS_FOR_PEER = 3;
|
37
|
+
const DEFAULT_MAX_PARALLEL_DIALS = 3;
|
38
|
+
|
39
|
+
const DEFAULT_PING_KEEP_ALIVE_SEC = 5 * 60;
|
40
|
+
const DEFAULT_RELAY_KEEP_ALIVE_SEC = 5 * 60;
|
41
|
+
|
42
|
+
type ConnectionManagerConstructorOptions = {
|
43
|
+
libp2p: Libp2p;
|
44
|
+
pubsubTopics: PubsubTopic[];
|
45
|
+
relay?: IRelay;
|
46
|
+
config?: Partial<ConnectionManagerOptions>;
|
47
|
+
};
|
29
48
|
|
30
49
|
export class ConnectionManager
|
31
50
|
extends TypedEventEmitter<IPeersByDiscoveryEvents & IConnectionStateEvents>
|
32
51
|
implements IConnectionManager
|
33
52
|
{
|
34
|
-
|
53
|
+
// TODO(weboko): make it private
|
54
|
+
public readonly pubsubTopics: PubsubTopic[];
|
55
|
+
|
35
56
|
private keepAliveManager: KeepAliveManager;
|
36
57
|
private options: ConnectionManagerOptions;
|
37
58
|
private libp2p: Libp2p;
|
@@ -51,29 +72,6 @@ export class ConnectionManager
|
|
51
72
|
return this.isP2PNetworkConnected;
|
52
73
|
}
|
53
74
|
|
54
|
-
public static create(
|
55
|
-
peerId: string,
|
56
|
-
libp2p: Libp2p,
|
57
|
-
keepAliveOptions: KeepAliveOptions,
|
58
|
-
pubsubTopics: PubsubTopic[],
|
59
|
-
relay?: IRelay,
|
60
|
-
options?: ConnectionManagerOptions
|
61
|
-
): ConnectionManager {
|
62
|
-
let instance = ConnectionManager.instances.get(peerId);
|
63
|
-
if (!instance) {
|
64
|
-
instance = new ConnectionManager(
|
65
|
-
libp2p,
|
66
|
-
keepAliveOptions,
|
67
|
-
pubsubTopics,
|
68
|
-
relay,
|
69
|
-
options
|
70
|
-
);
|
71
|
-
ConnectionManager.instances.set(peerId, instance);
|
72
|
-
}
|
73
|
-
|
74
|
-
return instance;
|
75
|
-
}
|
76
|
-
|
77
75
|
public stop(): void {
|
78
76
|
this.keepAliveManager.stopAll();
|
79
77
|
this.libp2p.removeEventListener(
|
@@ -156,27 +154,26 @@ export class ConnectionManager
|
|
156
154
|
};
|
157
155
|
}
|
158
156
|
|
159
|
-
|
160
|
-
libp2p: Libp2p,
|
161
|
-
keepAliveOptions: KeepAliveOptions,
|
162
|
-
public readonly configuredPubsubTopics: PubsubTopic[],
|
163
|
-
relay?: IRelay,
|
164
|
-
options?: Partial<ConnectionManagerOptions>
|
165
|
-
) {
|
157
|
+
public constructor(options: ConnectionManagerConstructorOptions) {
|
166
158
|
super();
|
167
|
-
this.libp2p = libp2p;
|
168
|
-
this.
|
159
|
+
this.libp2p = options.libp2p;
|
160
|
+
this.pubsubTopics = options.pubsubTopics;
|
169
161
|
this.options = {
|
170
162
|
maxDialAttemptsForPeer: DEFAULT_MAX_DIAL_ATTEMPTS_FOR_PEER,
|
171
163
|
maxBootstrapPeersAllowed: DEFAULT_MAX_BOOTSTRAP_PEERS_ALLOWED,
|
172
164
|
maxParallelDials: DEFAULT_MAX_PARALLEL_DIALS,
|
173
|
-
|
165
|
+
pingKeepAlive: DEFAULT_PING_KEEP_ALIVE_SEC,
|
166
|
+
relayKeepAlive: DEFAULT_RELAY_KEEP_ALIVE_SEC,
|
167
|
+
...options.config
|
174
168
|
};
|
175
169
|
|
176
170
|
this.keepAliveManager = new KeepAliveManager({
|
177
|
-
relay,
|
178
|
-
libp2p,
|
179
|
-
options:
|
171
|
+
relay: options.relay,
|
172
|
+
libp2p: options.libp2p,
|
173
|
+
options: {
|
174
|
+
pingKeepAlive: this.options.pingKeepAlive,
|
175
|
+
relayKeepAlive: this.options.relayKeepAlive
|
176
|
+
}
|
180
177
|
});
|
181
178
|
|
182
179
|
this.startEventListeners()
|
@@ -193,6 +190,29 @@ export class ConnectionManager
|
|
193
190
|
);
|
194
191
|
}
|
195
192
|
|
193
|
+
public async getConnectedPeers(codec?: string): Promise<Peer[]> {
|
194
|
+
const peerIDs = this.libp2p.getPeers();
|
195
|
+
|
196
|
+
if (peerIDs.length === 0) {
|
197
|
+
return [];
|
198
|
+
}
|
199
|
+
|
200
|
+
const peers = await Promise.all(
|
201
|
+
peerIDs.map(async (id) => {
|
202
|
+
try {
|
203
|
+
return await this.libp2p.peerStore.get(id);
|
204
|
+
} catch (e) {
|
205
|
+
return null;
|
206
|
+
}
|
207
|
+
})
|
208
|
+
);
|
209
|
+
|
210
|
+
return peers
|
211
|
+
.filter((p) => !!p)
|
212
|
+
.filter((p) => (codec ? (p as Peer).protocols.includes(codec) : true))
|
213
|
+
.sort((left, right) => getPeerPing(left) - getPeerPing(right)) as Peer[];
|
214
|
+
}
|
215
|
+
|
196
216
|
private async dialPeerStorePeers(): Promise<void> {
|
197
217
|
const peerInfos = await this.libp2p.peerStore.all();
|
198
218
|
const dialPromises = [];
|
@@ -219,15 +239,60 @@ export class ConnectionManager
|
|
219
239
|
this.startNetworkStatusListener();
|
220
240
|
}
|
221
241
|
|
222
|
-
|
242
|
+
/**
|
243
|
+
* Attempts to establish a connection with a peer and set up specified protocols.
|
244
|
+
* The method handles both PeerId and Multiaddr inputs, manages connection attempts,
|
245
|
+
* and maintains the connection state.
|
246
|
+
*
|
247
|
+
* The dialing process includes:
|
248
|
+
* 1. Converting input to dialable peer info
|
249
|
+
* 2. Managing parallel dial attempts
|
250
|
+
* 3. Attempting to establish protocol-specific connections
|
251
|
+
* 4. Handling connection failures and retries
|
252
|
+
* 5. Updating the peer store and connection state
|
253
|
+
*
|
254
|
+
* @param {PeerId | MultiaddrInput} peer - The peer to connect to, either as a PeerId or multiaddr
|
255
|
+
* @param {string[]} [protocolCodecs] - Optional array of protocol-specific codec strings to establish
|
256
|
+
* (e.g., for LightPush, Filter, Store protocols)
|
257
|
+
*
|
258
|
+
* @throws {Error} If the multiaddr is missing a peer ID
|
259
|
+
* @throws {Error} If the maximum dial attempts are reached and the peer cannot be dialed
|
260
|
+
* @throws {Error} If there's an error deleting an undialable peer from the peer store
|
261
|
+
*
|
262
|
+
* @example
|
263
|
+
* ```typescript
|
264
|
+
* // Dial using PeerId
|
265
|
+
* await connectionManager.dialPeer(peerId);
|
266
|
+
*
|
267
|
+
* // Dial using multiaddr with specific protocols
|
268
|
+
* await connectionManager.dialPeer(multiaddr, [
|
269
|
+
* "/vac/waku/relay/2.0.0",
|
270
|
+
* "/vac/waku/lightpush/2.0.0-beta1"
|
271
|
+
* ]);
|
272
|
+
* ```
|
273
|
+
*
|
274
|
+
* @remarks
|
275
|
+
* - The method implements exponential backoff through multiple dial attempts
|
276
|
+
* - Maintains a queue for parallel dial attempts (limited by maxParallelDials)
|
277
|
+
* - Integrates with the KeepAliveManager for connection maintenance
|
278
|
+
* - Updates the peer store and connection state after successful/failed attempts
|
279
|
+
* - If all dial attempts fail, triggers DNS discovery as a fallback
|
280
|
+
*/
|
281
|
+
public async dialPeer(peer: PeerId | MultiaddrInput): Promise<Connection> {
|
282
|
+
let connection: Connection | undefined;
|
283
|
+
let peerId: PeerId | undefined;
|
284
|
+
const peerDialInfo = this.getDialablePeerInfo(peer);
|
285
|
+
const peerIdStr = isPeerId(peerDialInfo)
|
286
|
+
? peerDialInfo.toString()
|
287
|
+
: peerDialInfo.getPeerId()!;
|
288
|
+
|
223
289
|
this.currentActiveParallelDialCount += 1;
|
224
290
|
let dialAttempt = 0;
|
225
291
|
while (dialAttempt < this.options.maxDialAttemptsForPeer) {
|
226
292
|
try {
|
227
|
-
log.info(
|
228
|
-
|
229
|
-
|
230
|
-
await this.libp2p.dial(peerId);
|
293
|
+
log.info(`Dialing peer ${peerDialInfo} on attempt ${dialAttempt + 1}`);
|
294
|
+
connection = await this.libp2p.dial(peerDialInfo);
|
295
|
+
peerId = connection.remotePeer;
|
231
296
|
|
232
297
|
const tags = await this.getTagNamesForPeer(peerId);
|
233
298
|
// add tag to connection describing discovery mechanism
|
@@ -246,21 +311,17 @@ export class ConnectionManager
|
|
246
311
|
} catch (error) {
|
247
312
|
if (error instanceof AggregateError) {
|
248
313
|
// Handle AggregateError
|
249
|
-
log.error(
|
250
|
-
`Error dialing peer ${peerId.toString()} - ${error.errors}`
|
251
|
-
);
|
314
|
+
log.error(`Error dialing peer ${peerIdStr} - ${error.errors}`);
|
252
315
|
} else {
|
253
316
|
// Handle generic error
|
254
317
|
log.error(
|
255
|
-
`Error dialing peer ${
|
256
|
-
(error as any).message
|
257
|
-
}`
|
318
|
+
`Error dialing peer ${peerIdStr} - ${(error as any).message}`
|
258
319
|
);
|
259
320
|
}
|
260
|
-
this.dialErrorsForPeer.set(
|
321
|
+
this.dialErrorsForPeer.set(peerIdStr, error);
|
261
322
|
|
262
323
|
dialAttempt++;
|
263
|
-
this.dialAttemptsForPeer.set(
|
324
|
+
this.dialAttemptsForPeer.set(peerIdStr, dialAttempt);
|
264
325
|
}
|
265
326
|
}
|
266
327
|
|
@@ -271,7 +332,7 @@ export class ConnectionManager
|
|
271
332
|
// If max dial attempts reached and dialing failed, delete the peer
|
272
333
|
if (dialAttempt === this.options.maxDialAttemptsForPeer) {
|
273
334
|
try {
|
274
|
-
const error = this.dialErrorsForPeer.get(
|
335
|
+
const error = this.dialErrorsForPeer.get(peerIdStr);
|
275
336
|
|
276
337
|
if (error) {
|
277
338
|
let errorMessage;
|
@@ -288,21 +349,65 @@ export class ConnectionManager
|
|
288
349
|
}
|
289
350
|
|
290
351
|
log.info(
|
291
|
-
`Deleting undialable peer ${
|
352
|
+
`Deleting undialable peer ${peerIdStr} from peer store. Reason: ${errorMessage}`
|
292
353
|
);
|
293
354
|
}
|
294
355
|
|
295
|
-
this.dialErrorsForPeer.delete(
|
296
|
-
|
356
|
+
this.dialErrorsForPeer.delete(peerIdStr);
|
357
|
+
if (peerId) {
|
358
|
+
await this.libp2p.peerStore.delete(peerId);
|
359
|
+
}
|
297
360
|
|
298
361
|
// if it was last available peer - attempt DNS discovery
|
299
362
|
await this.attemptDnsDiscovery();
|
300
363
|
} catch (error) {
|
301
364
|
throw new Error(
|
302
|
-
`Error deleting undialable peer ${
|
365
|
+
`Error deleting undialable peer ${peerIdStr} from peer store - ${error}`
|
303
366
|
);
|
304
367
|
}
|
305
368
|
}
|
369
|
+
|
370
|
+
if (!connection) {
|
371
|
+
throw new Error(`Failed to dial peer ${peerDialInfo}`);
|
372
|
+
}
|
373
|
+
|
374
|
+
return connection;
|
375
|
+
}
|
376
|
+
|
377
|
+
/**
|
378
|
+
* Dial a peer with specific protocols.
|
379
|
+
* This method is a raw proxy to the libp2p dialProtocol method.
|
380
|
+
* @param peer - The peer to connect to, either as a PeerId or multiaddr
|
381
|
+
* @param protocolCodecs - Optional array of protocol-specific codec strings to establish
|
382
|
+
* @returns A stream to the peer
|
383
|
+
*/
|
384
|
+
public async rawDialPeerWithProtocols(
|
385
|
+
peer: PeerId | MultiaddrInput,
|
386
|
+
protocolCodecs: string[]
|
387
|
+
): Promise<Stream> {
|
388
|
+
const peerDialInfo = this.getDialablePeerInfo(peer);
|
389
|
+
return await this.libp2p.dialProtocol(peerDialInfo, protocolCodecs);
|
390
|
+
}
|
391
|
+
|
392
|
+
/**
|
393
|
+
* Internal utility to extract a PeerId or Multiaddr from a peer input.
|
394
|
+
* This is used internally by the connection manager to handle different peer input formats.
|
395
|
+
* @internal
|
396
|
+
*/
|
397
|
+
private getDialablePeerInfo(
|
398
|
+
peer: PeerId | MultiaddrInput
|
399
|
+
): PeerId | Multiaddr {
|
400
|
+
if (isPeerId(peer)) {
|
401
|
+
return peer;
|
402
|
+
} else {
|
403
|
+
// peer is of MultiaddrInput type
|
404
|
+
const ma = multiaddr(peer);
|
405
|
+
const peerIdStr = ma.getPeerId();
|
406
|
+
if (!peerIdStr) {
|
407
|
+
throw new Error("Failed to dial multiaddr: missing peer ID");
|
408
|
+
}
|
409
|
+
return ma;
|
410
|
+
}
|
306
411
|
}
|
307
412
|
|
308
413
|
private async attemptDnsDiscovery(): Promise<void> {
|
@@ -478,7 +583,7 @@ export class ConnectionManager
|
|
478
583
|
|
479
584
|
log.warn(
|
480
585
|
`Discovered peer ${peerId.toString()} with ShardInfo ${shardInfo} is not part of any of the configured pubsub topics (${
|
481
|
-
this.
|
586
|
+
this.pubsubTopics
|
482
587
|
}).
|
483
588
|
Not dialing.`
|
484
589
|
);
|
@@ -573,7 +678,7 @@ export class ConnectionManager
|
|
573
678
|
const pubsubTopics = shardInfoToPubsubTopics(shardInfo);
|
574
679
|
|
575
680
|
const isTopicConfigured = pubsubTopics.some((topic) =>
|
576
|
-
this.
|
681
|
+
this.pubsubTopics.includes(topic)
|
577
682
|
);
|
578
683
|
return isTopicConfigured;
|
579
684
|
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export { ConnectionManager } from "./connection_manager.js";
|
@@ -1,14 +1,18 @@
|
|
1
1
|
import type { PeerId } from "@libp2p/interface";
|
2
2
|
import type { IRelay, Libp2p, PeerIdStr } from "@waku/interfaces";
|
3
|
-
import type { KeepAliveOptions } from "@waku/interfaces";
|
4
3
|
import { Logger, pubsubTopicToSingleShardInfo } from "@waku/utils";
|
5
4
|
import { utf8ToBytes } from "@waku/utils/bytes";
|
6
5
|
|
7
|
-
import { createEncoder } from "
|
6
|
+
import { createEncoder } from "../message/version_0.js";
|
8
7
|
|
9
|
-
|
8
|
+
const RelayPingContentTopic = "/relay-ping/1/ping/null";
|
10
9
|
const log = new Logger("keep-alive");
|
11
10
|
|
11
|
+
type KeepAliveOptions = {
|
12
|
+
pingKeepAlive: number;
|
13
|
+
relayKeepAlive: number;
|
14
|
+
};
|
15
|
+
|
12
16
|
type CreateKeepAliveManagerOptions = {
|
13
17
|
options: KeepAliveOptions;
|
14
18
|
libp2p: Libp2p;
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import type { Peer } from "@libp2p/interface";
|
2
|
+
import { bytesToUtf8 } from "@waku/utils/bytes";
|
3
|
+
|
4
|
+
/**
|
5
|
+
* Reads peer's metadata and retrieves ping value.
|
6
|
+
* @param peer Peer or null
|
7
|
+
* @returns -1 if no ping attached, otherwise returns ping value
|
8
|
+
*/
|
9
|
+
export const getPeerPing = (peer: Peer | null): number => {
|
10
|
+
if (!peer) {
|
11
|
+
return -1;
|
12
|
+
}
|
13
|
+
|
14
|
+
try {
|
15
|
+
const bytes = peer.metadata.get("ping");
|
16
|
+
|
17
|
+
if (!bytes) {
|
18
|
+
return -1;
|
19
|
+
}
|
20
|
+
|
21
|
+
return Number(bytesToUtf8(bytes));
|
22
|
+
} catch (e) {
|
23
|
+
return -1;
|
24
|
+
}
|
25
|
+
};
|
@@ -0,0 +1,315 @@
|
|
1
|
+
import type { PeerId, Stream } from "@libp2p/interface";
|
2
|
+
import type { IncomingStreamData } from "@libp2p/interface-internal";
|
3
|
+
import {
|
4
|
+
type ContentTopic,
|
5
|
+
type CoreProtocolResult,
|
6
|
+
type IBaseProtocolCore,
|
7
|
+
type Libp2p,
|
8
|
+
ProtocolError,
|
9
|
+
type PubsubTopic
|
10
|
+
} from "@waku/interfaces";
|
11
|
+
import { WakuMessage } from "@waku/proto";
|
12
|
+
import { Logger } from "@waku/utils";
|
13
|
+
import all from "it-all";
|
14
|
+
import * as lp from "it-length-prefixed";
|
15
|
+
import { pipe } from "it-pipe";
|
16
|
+
import { Uint8ArrayList } from "uint8arraylist";
|
17
|
+
|
18
|
+
import { BaseProtocol } from "../base_protocol.js";
|
19
|
+
|
20
|
+
import {
|
21
|
+
FilterPushRpc,
|
22
|
+
FilterSubscribeResponse,
|
23
|
+
FilterSubscribeRpc
|
24
|
+
} from "./filter_rpc.js";
|
25
|
+
|
26
|
+
const log = new Logger("filter:v2");
|
27
|
+
|
28
|
+
export const FilterCodecs = {
|
29
|
+
SUBSCRIBE: "/vac/waku/filter-subscribe/2.0.0-beta1",
|
30
|
+
PUSH: "/vac/waku/filter-push/2.0.0-beta1"
|
31
|
+
};
|
32
|
+
|
33
|
+
export class FilterCore extends BaseProtocol implements IBaseProtocolCore {
|
34
|
+
public constructor(
|
35
|
+
private handleIncomingMessage: (
|
36
|
+
pubsubTopic: PubsubTopic,
|
37
|
+
wakuMessage: WakuMessage,
|
38
|
+
peerIdStr: string
|
39
|
+
) => Promise<void>,
|
40
|
+
public readonly pubsubTopics: PubsubTopic[],
|
41
|
+
libp2p: Libp2p
|
42
|
+
) {
|
43
|
+
super(FilterCodecs.SUBSCRIBE, libp2p.components, pubsubTopics);
|
44
|
+
|
45
|
+
libp2p
|
46
|
+
.handle(FilterCodecs.PUSH, this.onRequest.bind(this), {
|
47
|
+
maxInboundStreams: 100
|
48
|
+
})
|
49
|
+
.catch((e) => {
|
50
|
+
log.error("Failed to register ", FilterCodecs.PUSH, e);
|
51
|
+
});
|
52
|
+
}
|
53
|
+
|
54
|
+
public async subscribe(
|
55
|
+
pubsubTopic: PubsubTopic,
|
56
|
+
peerId: PeerId,
|
57
|
+
contentTopics: ContentTopic[]
|
58
|
+
): Promise<CoreProtocolResult> {
|
59
|
+
const stream = await this.getStream(peerId);
|
60
|
+
|
61
|
+
const request = FilterSubscribeRpc.createSubscribeRequest(
|
62
|
+
pubsubTopic,
|
63
|
+
contentTopics
|
64
|
+
);
|
65
|
+
|
66
|
+
let res: Uint8ArrayList[] | undefined;
|
67
|
+
try {
|
68
|
+
res = await pipe(
|
69
|
+
[request.encode()],
|
70
|
+
lp.encode,
|
71
|
+
stream,
|
72
|
+
lp.decode,
|
73
|
+
async (source) => await all(source)
|
74
|
+
);
|
75
|
+
} catch (error) {
|
76
|
+
log.error("Failed to send subscribe request", error);
|
77
|
+
return {
|
78
|
+
success: null,
|
79
|
+
failure: {
|
80
|
+
error: ProtocolError.GENERIC_FAIL,
|
81
|
+
peerId: peerId
|
82
|
+
}
|
83
|
+
};
|
84
|
+
}
|
85
|
+
|
86
|
+
const { statusCode, requestId, statusDesc } =
|
87
|
+
FilterSubscribeResponse.decode(res[0].slice());
|
88
|
+
|
89
|
+
if (statusCode < 200 || statusCode >= 300) {
|
90
|
+
log.error(
|
91
|
+
`Filter subscribe request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
|
92
|
+
);
|
93
|
+
return {
|
94
|
+
failure: {
|
95
|
+
error: ProtocolError.REMOTE_PEER_REJECTED,
|
96
|
+
peerId: peerId
|
97
|
+
},
|
98
|
+
success: null
|
99
|
+
};
|
100
|
+
}
|
101
|
+
|
102
|
+
return {
|
103
|
+
failure: null,
|
104
|
+
success: peerId
|
105
|
+
};
|
106
|
+
}
|
107
|
+
|
108
|
+
public async unsubscribe(
|
109
|
+
pubsubTopic: PubsubTopic,
|
110
|
+
peerId: PeerId,
|
111
|
+
contentTopics: ContentTopic[]
|
112
|
+
): Promise<CoreProtocolResult> {
|
113
|
+
let stream: Stream | undefined;
|
114
|
+
try {
|
115
|
+
stream = await this.getStream(peerId);
|
116
|
+
} catch (error) {
|
117
|
+
log.error(
|
118
|
+
`Failed to get a stream for remote peer${peerId.toString()}`,
|
119
|
+
error
|
120
|
+
);
|
121
|
+
return {
|
122
|
+
success: null,
|
123
|
+
failure: {
|
124
|
+
error: ProtocolError.NO_STREAM_AVAILABLE,
|
125
|
+
peerId: peerId
|
126
|
+
}
|
127
|
+
};
|
128
|
+
}
|
129
|
+
|
130
|
+
const unsubscribeRequest = FilterSubscribeRpc.createUnsubscribeRequest(
|
131
|
+
pubsubTopic,
|
132
|
+
contentTopics
|
133
|
+
);
|
134
|
+
|
135
|
+
try {
|
136
|
+
await pipe([unsubscribeRequest.encode()], lp.encode, stream.sink);
|
137
|
+
} catch (error) {
|
138
|
+
log.error("Failed to send unsubscribe request", error);
|
139
|
+
return {
|
140
|
+
success: null,
|
141
|
+
failure: {
|
142
|
+
error: ProtocolError.GENERIC_FAIL,
|
143
|
+
peerId: peerId
|
144
|
+
}
|
145
|
+
};
|
146
|
+
}
|
147
|
+
|
148
|
+
return {
|
149
|
+
success: peerId,
|
150
|
+
failure: null
|
151
|
+
};
|
152
|
+
}
|
153
|
+
|
154
|
+
public async unsubscribeAll(
|
155
|
+
pubsubTopic: PubsubTopic,
|
156
|
+
peerId: PeerId
|
157
|
+
): Promise<CoreProtocolResult> {
|
158
|
+
const stream = await this.getStream(peerId);
|
159
|
+
|
160
|
+
const request = FilterSubscribeRpc.createUnsubscribeAllRequest(pubsubTopic);
|
161
|
+
|
162
|
+
const res = await pipe(
|
163
|
+
[request.encode()],
|
164
|
+
lp.encode,
|
165
|
+
stream,
|
166
|
+
lp.decode,
|
167
|
+
async (source) => await all(source)
|
168
|
+
);
|
169
|
+
|
170
|
+
if (!res || !res.length) {
|
171
|
+
return {
|
172
|
+
failure: {
|
173
|
+
error: ProtocolError.NO_RESPONSE,
|
174
|
+
peerId: peerId
|
175
|
+
},
|
176
|
+
success: null
|
177
|
+
};
|
178
|
+
}
|
179
|
+
|
180
|
+
const { statusCode, requestId, statusDesc } =
|
181
|
+
FilterSubscribeResponse.decode(res[0].slice());
|
182
|
+
|
183
|
+
if (statusCode < 200 || statusCode >= 300) {
|
184
|
+
log.error(
|
185
|
+
`Filter unsubscribe all request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
|
186
|
+
);
|
187
|
+
return {
|
188
|
+
failure: {
|
189
|
+
error: ProtocolError.REMOTE_PEER_REJECTED,
|
190
|
+
peerId: peerId
|
191
|
+
},
|
192
|
+
success: null
|
193
|
+
};
|
194
|
+
}
|
195
|
+
|
196
|
+
return {
|
197
|
+
failure: null,
|
198
|
+
success: peerId
|
199
|
+
};
|
200
|
+
}
|
201
|
+
|
202
|
+
public async ping(peerId: PeerId): Promise<CoreProtocolResult> {
|
203
|
+
let stream: Stream | undefined;
|
204
|
+
try {
|
205
|
+
stream = await this.getStream(peerId);
|
206
|
+
} catch (error) {
|
207
|
+
log.error(
|
208
|
+
`Failed to get a stream for remote peer${peerId.toString()}`,
|
209
|
+
error
|
210
|
+
);
|
211
|
+
return {
|
212
|
+
success: null,
|
213
|
+
failure: {
|
214
|
+
error: ProtocolError.NO_STREAM_AVAILABLE,
|
215
|
+
peerId: peerId
|
216
|
+
}
|
217
|
+
};
|
218
|
+
}
|
219
|
+
|
220
|
+
const request = FilterSubscribeRpc.createSubscriberPingRequest();
|
221
|
+
|
222
|
+
let res: Uint8ArrayList[] | undefined;
|
223
|
+
try {
|
224
|
+
res = await pipe(
|
225
|
+
[request.encode()],
|
226
|
+
lp.encode,
|
227
|
+
stream,
|
228
|
+
lp.decode,
|
229
|
+
async (source) => await all(source)
|
230
|
+
);
|
231
|
+
} catch (error) {
|
232
|
+
log.error("Failed to send ping request", error);
|
233
|
+
return {
|
234
|
+
success: null,
|
235
|
+
failure: {
|
236
|
+
error: ProtocolError.GENERIC_FAIL,
|
237
|
+
peerId: peerId
|
238
|
+
}
|
239
|
+
};
|
240
|
+
}
|
241
|
+
|
242
|
+
if (!res || !res.length) {
|
243
|
+
return {
|
244
|
+
success: null,
|
245
|
+
failure: {
|
246
|
+
error: ProtocolError.NO_RESPONSE,
|
247
|
+
peerId: peerId
|
248
|
+
}
|
249
|
+
};
|
250
|
+
}
|
251
|
+
|
252
|
+
const { statusCode, requestId, statusDesc } =
|
253
|
+
FilterSubscribeResponse.decode(res[0].slice());
|
254
|
+
|
255
|
+
if (statusCode < 200 || statusCode >= 300) {
|
256
|
+
log.error(
|
257
|
+
`Filter ping request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
|
258
|
+
);
|
259
|
+
return {
|
260
|
+
success: null,
|
261
|
+
failure: {
|
262
|
+
error: ProtocolError.REMOTE_PEER_REJECTED,
|
263
|
+
peerId: peerId
|
264
|
+
}
|
265
|
+
};
|
266
|
+
}
|
267
|
+
return {
|
268
|
+
success: peerId,
|
269
|
+
failure: null
|
270
|
+
};
|
271
|
+
}
|
272
|
+
|
273
|
+
private onRequest(streamData: IncomingStreamData): void {
|
274
|
+
const { connection, stream } = streamData;
|
275
|
+
const { remotePeer } = connection;
|
276
|
+
log.info(`Received message from ${remotePeer.toString()}`);
|
277
|
+
try {
|
278
|
+
pipe(stream, lp.decode, async (source) => {
|
279
|
+
for await (const bytes of source) {
|
280
|
+
const response = FilterPushRpc.decode(bytes.slice());
|
281
|
+
|
282
|
+
const { pubsubTopic, wakuMessage } = response;
|
283
|
+
|
284
|
+
if (!wakuMessage) {
|
285
|
+
log.error("Received empty message");
|
286
|
+
return;
|
287
|
+
}
|
288
|
+
|
289
|
+
if (!pubsubTopic) {
|
290
|
+
log.error("Pubsub topic missing from push message");
|
291
|
+
return;
|
292
|
+
}
|
293
|
+
|
294
|
+
await this.handleIncomingMessage(
|
295
|
+
pubsubTopic,
|
296
|
+
wakuMessage,
|
297
|
+
connection.remotePeer.toString()
|
298
|
+
);
|
299
|
+
}
|
300
|
+
}).then(
|
301
|
+
() => {
|
302
|
+
log.info("Receiving pipe closed.");
|
303
|
+
},
|
304
|
+
async (e) => {
|
305
|
+
log.error(
|
306
|
+
`Error with receiving pipe on peer:${connection.remotePeer.toString()} -- stream:${stream.id} -- protocol:${stream.protocol}: `,
|
307
|
+
e
|
308
|
+
);
|
309
|
+
}
|
310
|
+
);
|
311
|
+
} catch (e) {
|
312
|
+
log.error("Error decoding message", e);
|
313
|
+
}
|
314
|
+
}
|
315
|
+
}
|