@waku/core 0.0.22 → 0.0.24
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/CHANGELOG.md +61 -0
- package/bundle/base_protocol-2a0c882e.js +1250 -0
- package/bundle/{browser-bde977a3.js → browser-90197c87.js} +26 -1
- package/bundle/index.js +20048 -3236
- package/bundle/lib/base_protocol.js +2 -116
- package/bundle/lib/message/version_0.js +2 -2
- package/bundle/lib/predefined_bootstrap_nodes.js +6 -6
- package/bundle/{version_0-86411fdf.js → version_0-f4afd324.js} +907 -814
- package/dist/.tsbuildinfo +1 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/base_protocol.d.ts +18 -5
- package/dist/lib/base_protocol.js +25 -8
- package/dist/lib/base_protocol.js.map +1 -1
- package/dist/lib/connection_manager.d.ts +15 -3
- package/dist/lib/connection_manager.js +92 -34
- package/dist/lib/connection_manager.js.map +1 -1
- package/dist/lib/filter/filter_rpc.js +4 -4
- package/dist/lib/filter/index.d.ts +4 -0
- package/dist/lib/filter/index.js +38 -29
- package/dist/lib/filter/index.js.map +1 -1
- package/dist/lib/filterPeers.d.ts +10 -0
- package/dist/lib/filterPeers.js +31 -0
- package/dist/lib/filterPeers.js.map +1 -0
- package/dist/lib/keep_alive_manager.d.ts +4 -2
- package/dist/lib/keep_alive_manager.js +62 -19
- package/dist/lib/keep_alive_manager.js.map +1 -1
- package/dist/lib/light_push/index.js +85 -38
- package/dist/lib/light_push/index.js.map +1 -1
- package/dist/lib/light_push/push_rpc.d.ts +1 -1
- package/dist/lib/light_push/push_rpc.js +3 -3
- package/dist/lib/message/version_0.d.ts +13 -13
- package/dist/lib/message/version_0.js +21 -18
- package/dist/lib/message/version_0.js.map +1 -1
- package/dist/lib/predefined_bootstrap_nodes.js +6 -6
- package/dist/lib/store/history_rpc.d.ts +1 -1
- package/dist/lib/store/history_rpc.js +4 -4
- package/dist/lib/store/index.d.ts +1 -6
- package/dist/lib/store/index.js +91 -47
- package/dist/lib/store/index.js.map +1 -1
- package/dist/lib/stream_manager.d.ts +15 -0
- package/dist/lib/stream_manager.js +56 -0
- package/dist/lib/stream_manager.js.map +1 -0
- package/dist/lib/to_proto_message.js +1 -1
- package/dist/lib/wait_for_remote_peer.d.ts +2 -2
- package/dist/lib/wait_for_remote_peer.js +10 -7
- package/dist/lib/wait_for_remote_peer.js.map +1 -1
- package/dist/lib/waku.d.ts +6 -5
- package/dist/lib/waku.js +6 -4
- package/dist/lib/waku.js.map +1 -1
- package/package.json +17 -33
- package/src/index.ts +6 -9
- package/src/lib/base_protocol.ts +49 -18
- package/src/lib/connection_manager.ts +132 -41
- package/src/lib/filter/filter_rpc.ts +4 -4
- package/src/lib/filter/index.ts +53 -41
- package/src/lib/filterPeers.ts +43 -0
- package/src/lib/keep_alive_manager.ts +79 -22
- package/src/lib/light_push/index.ts +132 -51
- package/src/lib/light_push/push_rpc.ts +3 -3
- package/src/lib/message/version_0.ts +27 -15
- package/src/lib/predefined_bootstrap_nodes.ts +7 -7
- package/src/lib/store/history_rpc.ts +6 -6
- package/src/lib/store/index.ts +121 -63
- package/src/lib/stream_manager.ts +72 -0
- package/src/lib/to_proto_message.ts +1 -1
- package/src/lib/wait_for_remote_peer.ts +11 -8
- package/src/lib/waku.ts +7 -4
- package/dist/lib/push_or_init_map.d.ts +0 -1
- package/dist/lib/push_or_init_map.js +0 -9
- package/dist/lib/push_or_init_map.js.map +0 -1
- package/src/lib/push_or_init_map.ts +0 -13
package/src/lib/base_protocol.ts
CHANGED
@@ -1,13 +1,12 @@
|
|
1
|
-
import type {
|
2
|
-
import type {
|
3
|
-
import type { PeerId } from "@libp2p/interface
|
4
|
-
import { Peer, PeerStore } from "@libp2p/interface
|
1
|
+
import type { Libp2p } from "@libp2p/interface";
|
2
|
+
import type { Stream } from "@libp2p/interface/connection";
|
3
|
+
import type { PeerId } from "@libp2p/interface/peer-id";
|
4
|
+
import { Peer, PeerStore } from "@libp2p/interface/peer-store";
|
5
5
|
import type { IBaseProtocol, Libp2pComponents } from "@waku/interfaces";
|
6
|
-
import {
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
} from "@waku/utils/libp2p";
|
6
|
+
import { getPeersForProtocol, selectPeerForProtocol } from "@waku/utils/libp2p";
|
7
|
+
|
8
|
+
import { filterPeers } from "./filterPeers.js";
|
9
|
+
import { StreamManager } from "./stream_manager.js";
|
11
10
|
|
12
11
|
/**
|
13
12
|
* A class with predefined helpers, to be used as a base to implement Waku
|
@@ -16,14 +15,29 @@ import {
|
|
16
15
|
export class BaseProtocol implements IBaseProtocol {
|
17
16
|
public readonly addLibp2pEventListener: Libp2p["addEventListener"];
|
18
17
|
public readonly removeLibp2pEventListener: Libp2p["removeEventListener"];
|
18
|
+
protected streamManager: StreamManager;
|
19
19
|
|
20
|
-
constructor(
|
20
|
+
constructor(
|
21
|
+
public multicodec: string,
|
22
|
+
private components: Libp2pComponents
|
23
|
+
) {
|
21
24
|
this.addLibp2pEventListener = components.events.addEventListener.bind(
|
22
25
|
components.events
|
23
26
|
);
|
24
27
|
this.removeLibp2pEventListener = components.events.removeEventListener.bind(
|
25
28
|
components.events
|
26
29
|
);
|
30
|
+
|
31
|
+
this.streamManager = new StreamManager(
|
32
|
+
multicodec,
|
33
|
+
components.connectionManager.getConnections.bind(
|
34
|
+
components.connectionManager
|
35
|
+
),
|
36
|
+
this.addLibp2pEventListener
|
37
|
+
);
|
38
|
+
}
|
39
|
+
protected async getStream(peer: Peer): Promise<Stream> {
|
40
|
+
return this.streamManager.getStream(peer);
|
27
41
|
}
|
28
42
|
|
29
43
|
public get peerStore(): PeerStore {
|
@@ -47,15 +61,32 @@ export class BaseProtocol implements IBaseProtocol {
|
|
47
61
|
);
|
48
62
|
return peer;
|
49
63
|
}
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
64
|
+
|
65
|
+
/**
|
66
|
+
* Retrieves a list of peers based on the specified criteria.
|
67
|
+
*
|
68
|
+
* @param numPeers - The total number of peers to retrieve. If 0, all peers are returned.
|
69
|
+
* @param maxBootstrapPeers - The maximum number of bootstrap peers to retrieve.
|
70
|
+
* @returns A Promise that resolves to an array of peers based on the specified criteria.
|
71
|
+
*/
|
72
|
+
protected async getPeers(
|
73
|
+
{
|
74
|
+
numPeers,
|
75
|
+
maxBootstrapPeers
|
76
|
+
}: {
|
77
|
+
numPeers: number;
|
78
|
+
maxBootstrapPeers: number;
|
79
|
+
} = {
|
80
|
+
maxBootstrapPeers: 1,
|
81
|
+
numPeers: 0
|
57
82
|
}
|
83
|
+
): Promise<Peer[]> {
|
84
|
+
// Retrieve all peers that support the protocol
|
85
|
+
const allPeersForProtocol = await getPeersForProtocol(this.peerStore, [
|
86
|
+
this.multicodec
|
87
|
+
]);
|
58
88
|
|
59
|
-
|
89
|
+
// Filter the peers based on the specified criteria
|
90
|
+
return filterPeers(allPeersForProtocol, numPeers, maxBootstrapPeers);
|
60
91
|
}
|
61
92
|
}
|
@@ -1,7 +1,9 @@
|
|
1
|
-
import type { PeerId } from "@libp2p/interface
|
2
|
-
import type { PeerInfo } from "@libp2p/interface
|
3
|
-
import type { Peer } from "@libp2p/interface
|
1
|
+
import type { PeerId } from "@libp2p/interface/peer-id";
|
2
|
+
import type { PeerInfo } from "@libp2p/interface/peer-info";
|
3
|
+
import type { Peer } from "@libp2p/interface/peer-store";
|
4
|
+
import type { PeerStore } from "@libp2p/interface/peer-store";
|
4
5
|
import { CustomEvent, EventEmitter } from "@libp2p/interfaces/events";
|
6
|
+
import { decodeRelayShard } from "@waku/enr";
|
5
7
|
import {
|
6
8
|
ConnectionManagerOptions,
|
7
9
|
EPeersByDiscoveryEvents,
|
@@ -10,8 +12,11 @@ import {
|
|
10
12
|
IRelay,
|
11
13
|
KeepAliveOptions,
|
12
14
|
PeersByDiscoveryResult,
|
15
|
+
PubSubTopic,
|
16
|
+
ShardInfo
|
13
17
|
} from "@waku/interfaces";
|
14
18
|
import { Libp2p, Tags } from "@waku/interfaces";
|
19
|
+
import { shardInfoToPubSubTopics } from "@waku/utils";
|
15
20
|
import debug from "debug";
|
16
21
|
|
17
22
|
import { KeepAliveManager } from "./keep_alive_manager.js";
|
@@ -40,6 +45,7 @@ export class ConnectionManager
|
|
40
45
|
peerId: string,
|
41
46
|
libp2p: Libp2p,
|
42
47
|
keepAliveOptions: KeepAliveOptions,
|
48
|
+
pubsubTopics: PubSubTopic[],
|
43
49
|
relay?: IRelay,
|
44
50
|
options?: ConnectionManagerOptions
|
45
51
|
): ConnectionManager {
|
@@ -48,6 +54,7 @@ export class ConnectionManager
|
|
48
54
|
instance = new ConnectionManager(
|
49
55
|
libp2p,
|
50
56
|
keepAliveOptions,
|
57
|
+
pubsubTopics,
|
51
58
|
relay,
|
52
59
|
options
|
53
60
|
);
|
@@ -92,28 +99,30 @@ export class ConnectionManager
|
|
92
99
|
return {
|
93
100
|
DISCOVERED: {
|
94
101
|
[Tags.BOOTSTRAP]: peersDiscoveredByBootstrap,
|
95
|
-
[Tags.PEER_EXCHANGE]: peersDiscoveredByPeerExchange
|
102
|
+
[Tags.PEER_EXCHANGE]: peersDiscoveredByPeerExchange
|
96
103
|
},
|
97
104
|
CONNECTED: {
|
98
105
|
[Tags.BOOTSTRAP]: peersConnectedByBootstrap,
|
99
|
-
[Tags.PEER_EXCHANGE]: peersConnectedByPeerExchange
|
100
|
-
}
|
106
|
+
[Tags.PEER_EXCHANGE]: peersConnectedByPeerExchange
|
107
|
+
}
|
101
108
|
};
|
102
109
|
}
|
103
110
|
|
104
111
|
private constructor(
|
105
112
|
libp2p: Libp2p,
|
106
113
|
keepAliveOptions: KeepAliveOptions,
|
114
|
+
private configuredPubSubTopics: PubSubTopic[],
|
107
115
|
relay?: IRelay,
|
108
116
|
options?: Partial<ConnectionManagerOptions>
|
109
117
|
) {
|
110
118
|
super();
|
111
119
|
this.libp2p = libp2p;
|
120
|
+
this.configuredPubSubTopics = configuredPubSubTopics;
|
112
121
|
this.options = {
|
113
122
|
maxDialAttemptsForPeer: DEFAULT_MAX_DIAL_ATTEMPTS_FOR_PEER,
|
114
123
|
maxBootstrapPeersAllowed: DEFAULT_MAX_BOOTSTRAP_PEERS_ALLOWED,
|
115
124
|
maxParallelDials: DEFAULT_MAX_PARALLEL_DIALS,
|
116
|
-
...options
|
125
|
+
...options
|
117
126
|
};
|
118
127
|
|
119
128
|
this.keepAliveManager = new KeepAliveManager(keepAliveOptions, relay);
|
@@ -217,16 +226,24 @@ export class ConnectionManager
|
|
217
226
|
try {
|
218
227
|
const error = this.dialErrorsForPeer.get(peerId.toString());
|
219
228
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
229
|
+
if (error) {
|
230
|
+
let errorMessage;
|
231
|
+
if (error instanceof AggregateError) {
|
232
|
+
if (!error.errors) {
|
233
|
+
log(`No errors array found for AggregateError`);
|
234
|
+
} else if (error.errors.length === 0) {
|
235
|
+
log(`Errors array is empty for AggregateError`);
|
236
|
+
} else {
|
237
|
+
errorMessage = JSON.stringify(error.errors[0]);
|
238
|
+
}
|
239
|
+
} else {
|
240
|
+
errorMessage = error.message;
|
241
|
+
}
|
226
242
|
|
227
|
-
|
228
|
-
|
229
|
-
|
243
|
+
log(
|
244
|
+
`Deleting undialable peer ${peerId.toString()} from peer store. Error: ${errorMessage}`
|
245
|
+
);
|
246
|
+
}
|
230
247
|
|
231
248
|
this.dialErrorsForPeer.delete(peerId.toString());
|
232
249
|
await this.libp2p.peerStore.delete(peerId);
|
@@ -297,15 +314,15 @@ export class ConnectionManager
|
|
297
314
|
}
|
298
315
|
|
299
316
|
private async attemptDial(peerId: PeerId): Promise<void> {
|
317
|
+
if (!(await this.shouldDialPeer(peerId))) return;
|
318
|
+
|
300
319
|
if (this.currentActiveDialCount >= this.options.maxParallelDials) {
|
301
320
|
this.pendingPeerDialQueue.push(peerId);
|
302
321
|
return;
|
303
322
|
}
|
304
323
|
|
305
|
-
if (!(await this.shouldDialPeer(peerId))) return;
|
306
|
-
|
307
324
|
this.dialPeer(peerId).catch((err) => {
|
308
|
-
|
325
|
+
log(`Error dialing peer ${peerId.toString()} : ${err}`);
|
309
326
|
});
|
310
327
|
}
|
311
328
|
|
@@ -314,20 +331,7 @@ export class ConnectionManager
|
|
314
331
|
void (async () => {
|
315
332
|
const { id: peerId } = evt.detail;
|
316
333
|
|
317
|
-
|
318
|
-
Tags.BOOTSTRAP
|
319
|
-
);
|
320
|
-
|
321
|
-
this.dispatchEvent(
|
322
|
-
new CustomEvent<PeerId>(
|
323
|
-
isBootstrap
|
324
|
-
? EPeersByDiscoveryEvents.PEER_DISCOVERY_BOOTSTRAP
|
325
|
-
: EPeersByDiscoveryEvents.PEER_DISCOVERY_PEER_EXCHANGE,
|
326
|
-
{
|
327
|
-
detail: peerId,
|
328
|
-
}
|
329
|
-
)
|
330
|
-
);
|
334
|
+
await this.dispatchDiscoveryEvent(peerId);
|
331
335
|
|
332
336
|
try {
|
333
337
|
await this.attemptDial(peerId);
|
@@ -340,7 +344,11 @@ export class ConnectionManager
|
|
340
344
|
void (async () => {
|
341
345
|
const peerId = evt.detail;
|
342
346
|
|
343
|
-
this.keepAliveManager.start(
|
347
|
+
this.keepAliveManager.start(
|
348
|
+
peerId,
|
349
|
+
this.libp2p.services.ping,
|
350
|
+
this.libp2p.peerStore
|
351
|
+
);
|
344
352
|
|
345
353
|
const isBootstrap = (await this.getTagNamesForPeer(peerId)).includes(
|
346
354
|
Tags.BOOTSTRAP
|
@@ -361,7 +369,7 @@ export class ConnectionManager
|
|
361
369
|
new CustomEvent<PeerId>(
|
362
370
|
EPeersByDiscoveryEvents.PEER_CONNECT_BOOTSTRAP,
|
363
371
|
{
|
364
|
-
detail: peerId
|
372
|
+
detail: peerId
|
365
373
|
}
|
366
374
|
)
|
367
375
|
);
|
@@ -371,7 +379,7 @@ export class ConnectionManager
|
|
371
379
|
new CustomEvent<PeerId>(
|
372
380
|
EPeersByDiscoveryEvents.PEER_CONNECT_PEER_EXCHANGE,
|
373
381
|
{
|
374
|
-
detail: peerId
|
382
|
+
detail: peerId
|
375
383
|
}
|
376
384
|
)
|
377
385
|
);
|
@@ -382,19 +390,58 @@ export class ConnectionManager
|
|
382
390
|
return (evt: CustomEvent<PeerId>): void => {
|
383
391
|
this.keepAliveManager.stop(evt.detail);
|
384
392
|
};
|
385
|
-
}
|
393
|
+
}
|
386
394
|
};
|
387
395
|
|
388
396
|
/**
|
389
|
-
* Checks if the peer
|
390
|
-
* 1. If the peer is
|
391
|
-
* 2. If the peer is not
|
397
|
+
* Checks if the peer should be dialed based on the following conditions:
|
398
|
+
* 1. If the peer is already connected, don't dial
|
399
|
+
* 2. If the peer is not part of any of the configured pubsub topics, don't dial
|
400
|
+
* 3. If the peer is not dialable based on bootstrap status, don't dial
|
401
|
+
* @returns true if the peer should be dialed, false otherwise
|
392
402
|
*/
|
393
403
|
private async shouldDialPeer(peerId: PeerId): Promise<boolean> {
|
404
|
+
// if we're already connected to the peer, don't dial
|
394
405
|
const isConnected = this.libp2p.getConnections(peerId).length > 0;
|
406
|
+
if (isConnected) {
|
407
|
+
log(`Already connected to peer ${peerId.toString()}. Not dialing.`);
|
408
|
+
return false;
|
409
|
+
}
|
410
|
+
|
411
|
+
// if the peer is not part of any of the configured pubsub topics, don't dial
|
412
|
+
if (!(await this.isPeerTopicConfigured(peerId))) {
|
413
|
+
const shardInfo = await this.getPeerShardInfo(
|
414
|
+
peerId,
|
415
|
+
this.libp2p.peerStore
|
416
|
+
);
|
417
|
+
log(
|
418
|
+
`Discovered peer ${peerId.toString()} with ShardInfo ${shardInfo} is not part of any of the configured pubsub topics (${
|
419
|
+
this.configuredPubSubTopics
|
420
|
+
}).
|
421
|
+
Not dialing.`
|
422
|
+
);
|
423
|
+
return false;
|
424
|
+
}
|
395
425
|
|
396
|
-
if
|
426
|
+
// if the peer is not dialable based on bootstrap status, don't dial
|
427
|
+
if (!(await this.isPeerDialableBasedOnBootstrapStatus(peerId))) {
|
428
|
+
log(
|
429
|
+
`Peer ${peerId.toString()} is not dialable based on bootstrap status. Not dialing.`
|
430
|
+
);
|
431
|
+
return false;
|
432
|
+
}
|
397
433
|
|
434
|
+
return true;
|
435
|
+
}
|
436
|
+
|
437
|
+
/**
|
438
|
+
* Checks if the peer is dialable based on the following conditions:
|
439
|
+
* 1. If the peer is a bootstrap peer, it is only dialable if the number of current bootstrap connections is less than the max allowed.
|
440
|
+
* 2. If the peer is not a bootstrap peer
|
441
|
+
*/
|
442
|
+
private async isPeerDialableBasedOnBootstrapStatus(
|
443
|
+
peerId: PeerId
|
444
|
+
): Promise<boolean> {
|
398
445
|
const tagNames = await this.getTagNamesForPeer(peerId);
|
399
446
|
|
400
447
|
const isBootstrap = tagNames.some((tagName) => tagName === Tags.BOOTSTRAP);
|
@@ -414,6 +461,23 @@ export class ConnectionManager
|
|
414
461
|
return false;
|
415
462
|
}
|
416
463
|
|
464
|
+
private async dispatchDiscoveryEvent(peerId: PeerId): Promise<void> {
|
465
|
+
const isBootstrap = (await this.getTagNamesForPeer(peerId)).includes(
|
466
|
+
Tags.BOOTSTRAP
|
467
|
+
);
|
468
|
+
|
469
|
+
this.dispatchEvent(
|
470
|
+
new CustomEvent<PeerId>(
|
471
|
+
isBootstrap
|
472
|
+
? EPeersByDiscoveryEvents.PEER_DISCOVERY_BOOTSTRAP
|
473
|
+
: EPeersByDiscoveryEvents.PEER_DISCOVERY_PEER_EXCHANGE,
|
474
|
+
{
|
475
|
+
detail: peerId
|
476
|
+
}
|
477
|
+
)
|
478
|
+
);
|
479
|
+
}
|
480
|
+
|
417
481
|
/**
|
418
482
|
* Fetches the tag names for a given peer
|
419
483
|
*/
|
@@ -426,4 +490,31 @@ export class ConnectionManager
|
|
426
490
|
return [];
|
427
491
|
}
|
428
492
|
}
|
493
|
+
|
494
|
+
private async isPeerTopicConfigured(peerId: PeerId): Promise<boolean> {
|
495
|
+
const shardInfo = await this.getPeerShardInfo(
|
496
|
+
peerId,
|
497
|
+
this.libp2p.peerStore
|
498
|
+
);
|
499
|
+
|
500
|
+
// If there's no shard information, simply return true
|
501
|
+
if (!shardInfo) return true;
|
502
|
+
|
503
|
+
const pubsubTopics = shardInfoToPubSubTopics(shardInfo);
|
504
|
+
|
505
|
+
const isTopicConfigured = pubsubTopics.some((topic) =>
|
506
|
+
this.configuredPubSubTopics.includes(topic)
|
507
|
+
);
|
508
|
+
return isTopicConfigured;
|
509
|
+
}
|
510
|
+
|
511
|
+
private async getPeerShardInfo(
|
512
|
+
peerId: PeerId,
|
513
|
+
peerStore: PeerStore
|
514
|
+
): Promise<ShardInfo | undefined> {
|
515
|
+
const peer = await peerStore.get(peerId);
|
516
|
+
const shardInfoBytes = peer.metadata.get("shardInfo");
|
517
|
+
if (!shardInfoBytes) return undefined;
|
518
|
+
return decodeRelayShard(shardInfoBytes);
|
519
|
+
}
|
429
520
|
}
|
@@ -42,7 +42,7 @@ export class FilterSubscribeRpc {
|
|
42
42
|
filterSubscribeType:
|
43
43
|
proto.FilterSubscribeRequest.FilterSubscribeType.SUBSCRIBE,
|
44
44
|
pubsubTopic,
|
45
|
-
contentTopics
|
45
|
+
contentTopics
|
46
46
|
});
|
47
47
|
}
|
48
48
|
|
@@ -55,7 +55,7 @@ export class FilterSubscribeRpc {
|
|
55
55
|
filterSubscribeType:
|
56
56
|
proto.FilterSubscribeRequest.FilterSubscribeType.UNSUBSCRIBE,
|
57
57
|
pubsubTopic,
|
58
|
-
contentTopics
|
58
|
+
contentTopics
|
59
59
|
});
|
60
60
|
}
|
61
61
|
|
@@ -65,7 +65,7 @@ export class FilterSubscribeRpc {
|
|
65
65
|
filterSubscribeType:
|
66
66
|
proto.FilterSubscribeRequest.FilterSubscribeType.UNSUBSCRIBE_ALL,
|
67
67
|
pubsubTopic,
|
68
|
-
contentTopics: []
|
68
|
+
contentTopics: []
|
69
69
|
});
|
70
70
|
}
|
71
71
|
|
@@ -75,7 +75,7 @@ export class FilterSubscribeRpc {
|
|
75
75
|
filterSubscribeType:
|
76
76
|
proto.FilterSubscribeRequest.FilterSubscribeType.SUBSCRIBER_PING,
|
77
77
|
pubsubTopic: "",
|
78
|
-
contentTopics: []
|
78
|
+
contentTopics: []
|
79
79
|
});
|
80
80
|
}
|
81
81
|
|
package/src/lib/filter/index.ts
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
import { Stream } from "@libp2p/interface
|
2
|
-
import type {
|
3
|
-
import type {
|
4
|
-
import type { IncomingStreamData } from "@libp2p/interface-registrar";
|
1
|
+
import { Stream } from "@libp2p/interface/connection";
|
2
|
+
import type { Peer } from "@libp2p/interface/peer-store";
|
3
|
+
import type { IncomingStreamData } from "@libp2p/interface-internal/registrar";
|
5
4
|
import type {
|
6
5
|
Callback,
|
7
6
|
ContentTopic,
|
@@ -14,12 +13,15 @@ import type {
|
|
14
13
|
Libp2p,
|
15
14
|
PeerIdStr,
|
16
15
|
ProtocolCreateOptions,
|
17
|
-
ProtocolOptions,
|
18
16
|
PubSubTopic,
|
19
|
-
Unsubscribe
|
17
|
+
Unsubscribe
|
20
18
|
} from "@waku/interfaces";
|
21
19
|
import { WakuMessage } from "@waku/proto";
|
22
|
-
import {
|
20
|
+
import {
|
21
|
+
ensurePubsubTopicIsConfigured,
|
22
|
+
groupByContentTopic,
|
23
|
+
toAsyncIterator
|
24
|
+
} from "@waku/utils";
|
23
25
|
import debug from "debug";
|
24
26
|
import all from "it-all";
|
25
27
|
import * as lp from "it-length-prefixed";
|
@@ -31,7 +33,7 @@ import { DefaultPubSubTopic } from "../constants.js";
|
|
31
33
|
import {
|
32
34
|
FilterPushRpc,
|
33
35
|
FilterSubscribeResponse,
|
34
|
-
FilterSubscribeRpc
|
36
|
+
FilterSubscribeRpc
|
35
37
|
} from "./filter_rpc.js";
|
36
38
|
|
37
39
|
const log = debug("waku:filter:v2");
|
@@ -41,14 +43,14 @@ type SubscriptionCallback<T extends IDecodedMessage> = {
|
|
41
43
|
callback: Callback<T>;
|
42
44
|
};
|
43
45
|
|
44
|
-
const FilterCodecs = {
|
46
|
+
export const FilterCodecs = {
|
45
47
|
SUBSCRIBE: "/vac/waku/filter-subscribe/2.0.0-beta1",
|
46
|
-
PUSH: "/vac/waku/filter-push/2.0.0-beta1"
|
48
|
+
PUSH: "/vac/waku/filter-push/2.0.0-beta1"
|
47
49
|
};
|
48
50
|
|
49
51
|
class Subscription {
|
50
52
|
private readonly peer: Peer;
|
51
|
-
private readonly
|
53
|
+
private readonly pubsubTopic: PubSubTopic;
|
52
54
|
private newStream: (peer: Peer) => Promise<Stream>;
|
53
55
|
|
54
56
|
private subscriptionCallbacks: Map<
|
@@ -57,12 +59,12 @@ class Subscription {
|
|
57
59
|
>;
|
58
60
|
|
59
61
|
constructor(
|
60
|
-
|
62
|
+
pubsubTopic: PubSubTopic,
|
61
63
|
remotePeer: Peer,
|
62
64
|
newStream: (peer: Peer) => Promise<Stream>
|
63
65
|
) {
|
64
66
|
this.peer = remotePeer;
|
65
|
-
this.
|
67
|
+
this.pubsubTopic = pubsubTopic;
|
66
68
|
this.newStream = newStream;
|
67
69
|
this.subscriptionCallbacks = new Map();
|
68
70
|
}
|
@@ -78,7 +80,7 @@ class Subscription {
|
|
78
80
|
const stream = await this.newStream(this.peer);
|
79
81
|
|
80
82
|
const request = FilterSubscribeRpc.createSubscribeRequest(
|
81
|
-
this.
|
83
|
+
this.pubsubTopic,
|
82
84
|
contentTopics
|
83
85
|
);
|
84
86
|
|
@@ -91,6 +93,12 @@ class Subscription {
|
|
91
93
|
async (source) => await all(source)
|
92
94
|
);
|
93
95
|
|
96
|
+
if (!res || !res.length) {
|
97
|
+
throw Error(
|
98
|
+
`No response received for request ${request.requestId}: ${res}`
|
99
|
+
);
|
100
|
+
}
|
101
|
+
|
94
102
|
const { statusCode, requestId, statusDesc } =
|
95
103
|
FilterSubscribeResponse.decode(res[0].slice());
|
96
104
|
|
@@ -125,7 +133,7 @@ class Subscription {
|
|
125
133
|
// Decoder that decode to different implementations of `IDecodedMessage`
|
126
134
|
const subscriptionCallback = {
|
127
135
|
decoders,
|
128
|
-
callback
|
136
|
+
callback
|
129
137
|
} as unknown as SubscriptionCallback<IDecodedMessage>;
|
130
138
|
|
131
139
|
// The callback and decoder may override previous values, this is on
|
@@ -137,7 +145,7 @@ class Subscription {
|
|
137
145
|
async unsubscribe(contentTopics: ContentTopic[]): Promise<void> {
|
138
146
|
const stream = await this.newStream(this.peer);
|
139
147
|
const unsubscribeRequest = FilterSubscribeRpc.createUnsubscribeRequest(
|
140
|
-
this.
|
148
|
+
this.pubsubTopic,
|
141
149
|
contentTopics
|
142
150
|
);
|
143
151
|
|
@@ -186,7 +194,7 @@ class Subscription {
|
|
186
194
|
const stream = await this.newStream(this.peer);
|
187
195
|
|
188
196
|
const request = FilterSubscribeRpc.createUnsubscribeAllRequest(
|
189
|
-
this.
|
197
|
+
this.pubsubTopic
|
190
198
|
);
|
191
199
|
|
192
200
|
try {
|
@@ -221,67 +229,72 @@ class Subscription {
|
|
221
229
|
log("No subscription callback available for ", contentTopic);
|
222
230
|
return;
|
223
231
|
}
|
224
|
-
await pushMessage(subscriptionCallback, this.
|
232
|
+
await pushMessage(subscriptionCallback, this.pubsubTopic, message);
|
225
233
|
}
|
226
234
|
}
|
227
235
|
|
228
236
|
class Filter extends BaseProtocol implements IReceiver {
|
229
|
-
private readonly
|
237
|
+
private readonly pubsubTopics: PubSubTopic[] = [];
|
230
238
|
private activeSubscriptions = new Map<string, Subscription>();
|
239
|
+
private readonly NUM_PEERS_PROTOCOL = 1;
|
231
240
|
|
232
241
|
private getActiveSubscription(
|
233
|
-
|
242
|
+
pubsubTopic: PubSubTopic,
|
234
243
|
peerIdStr: PeerIdStr
|
235
244
|
): Subscription | undefined {
|
236
|
-
return this.activeSubscriptions.get(`${
|
245
|
+
return this.activeSubscriptions.get(`${pubsubTopic}_${peerIdStr}`);
|
237
246
|
}
|
238
247
|
|
239
248
|
private setActiveSubscription(
|
240
|
-
|
249
|
+
pubsubTopic: PubSubTopic,
|
241
250
|
peerIdStr: PeerIdStr,
|
242
251
|
subscription: Subscription
|
243
252
|
): Subscription {
|
244
|
-
this.activeSubscriptions.set(`${
|
253
|
+
this.activeSubscriptions.set(`${pubsubTopic}_${peerIdStr}`, subscription);
|
245
254
|
return subscription;
|
246
255
|
}
|
247
256
|
|
248
257
|
constructor(libp2p: Libp2p, options?: ProtocolCreateOptions) {
|
249
258
|
super(FilterCodecs.SUBSCRIBE, libp2p.components);
|
250
259
|
|
260
|
+
this.pubsubTopics = options?.pubsubTopics || [DefaultPubSubTopic];
|
261
|
+
|
251
262
|
libp2p.handle(FilterCodecs.PUSH, this.onRequest.bind(this)).catch((e) => {
|
252
263
|
log("Failed to register ", FilterCodecs.PUSH, e);
|
253
264
|
});
|
254
265
|
|
255
266
|
this.activeSubscriptions = new Map();
|
256
|
-
|
257
|
-
this.options = options ?? {};
|
258
267
|
}
|
259
268
|
|
260
269
|
async createSubscription(
|
261
|
-
|
262
|
-
peerId?: PeerId
|
270
|
+
pubsubTopic: string = DefaultPubSubTopic
|
263
271
|
): Promise<Subscription> {
|
264
|
-
|
265
|
-
pubSubTopic ?? this.options.pubSubTopic ?? DefaultPubSubTopic;
|
272
|
+
ensurePubsubTopicIsConfigured(pubsubTopic, this.pubsubTopics);
|
266
273
|
|
267
|
-
|
274
|
+
//TODO: get a relevant peer for the topic/shard
|
275
|
+
// https://github.com/waku-org/js-waku/pull/1586#discussion_r1336428230
|
276
|
+
const peer = (
|
277
|
+
await this.getPeers({
|
278
|
+
maxBootstrapPeers: 1,
|
279
|
+
numPeers: this.NUM_PEERS_PROTOCOL
|
280
|
+
})
|
281
|
+
)[0];
|
268
282
|
|
269
283
|
const subscription =
|
270
|
-
this.getActiveSubscription(
|
284
|
+
this.getActiveSubscription(pubsubTopic, peer.id.toString()) ??
|
271
285
|
this.setActiveSubscription(
|
272
|
-
|
286
|
+
pubsubTopic,
|
273
287
|
peer.id.toString(),
|
274
|
-
new Subscription(
|
288
|
+
new Subscription(pubsubTopic, peer, this.getStream.bind(this, peer))
|
275
289
|
);
|
276
290
|
|
277
291
|
return subscription;
|
278
292
|
}
|
279
293
|
|
280
294
|
public toSubscriptionIterator<T extends IDecodedMessage>(
|
281
|
-
decoders: IDecoder<T> | IDecoder<T>[]
|
282
|
-
opts?: ProtocolOptions | undefined
|
295
|
+
decoders: IDecoder<T> | IDecoder<T>[]
|
283
296
|
): Promise<IAsyncIterator<T>> {
|
284
|
-
return toAsyncIterator(this, decoders
|
297
|
+
return toAsyncIterator(this, decoders);
|
285
298
|
}
|
286
299
|
|
287
300
|
/**
|
@@ -301,10 +314,9 @@ class Filter extends BaseProtocol implements IReceiver {
|
|
301
314
|
*/
|
302
315
|
async subscribe<T extends IDecodedMessage>(
|
303
316
|
decoders: IDecoder<T> | IDecoder<T>[],
|
304
|
-
callback: Callback<T
|
305
|
-
opts?: ProtocolOptions
|
317
|
+
callback: Callback<T>
|
306
318
|
): Promise<Unsubscribe> {
|
307
|
-
const subscription = await this.createSubscription(
|
319
|
+
const subscription = await this.createSubscription();
|
308
320
|
|
309
321
|
await subscription.subscribe(decoders, callback);
|
310
322
|
|
@@ -373,7 +385,7 @@ export function wakuFilter(
|
|
373
385
|
|
374
386
|
async function pushMessage<T extends IDecodedMessage>(
|
375
387
|
subscriptionCallback: SubscriptionCallback<T>,
|
376
|
-
|
388
|
+
pubsubTopic: PubSubTopic,
|
377
389
|
message: WakuMessage
|
378
390
|
): Promise<void> {
|
379
391
|
const { decoders, callback } = subscriptionCallback;
|
@@ -387,7 +399,7 @@ async function pushMessage<T extends IDecodedMessage>(
|
|
387
399
|
try {
|
388
400
|
const decodePromises = decoders.map((dec) =>
|
389
401
|
dec
|
390
|
-
.fromProtoObj(
|
402
|
+
.fromProtoObj(pubsubTopic, message as IProtoMessage)
|
391
403
|
.then((decoded) => decoded || Promise.reject("Decoding failed"))
|
392
404
|
);
|
393
405
|
|