Skip to content

Commit 477c2a5

Browse files
feat!: protocols filter peers as per configured shard (#1756)
* merge: master * fix: tests * update: interfafces * rm: comments * metadata: store peerIdStr instead of peerId * chore(utils): move fast-utils to dev deps * fix: allow autosharding nodes to get peers (#1785) * fix: merge * fix: build * fix: failing tests from master merge --------- Co-authored-by: Arseniy Klempner <arseniyk@status.im>
1 parent bb680e4 commit 477c2a5

34 files changed

+664
-129
lines changed

package-lock.json

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/core/src/index.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ export * as waku_filter from "./lib/filter/index.js";
1414
export { wakuFilter, FilterCodecs } from "./lib/filter/index.js";
1515

1616
export * as waku_light_push from "./lib/light_push/index.js";
17-
export { LightPushCodec } from "./lib/light_push/index.js";
18-
export { wakuLightPush } from "./lib/light_push/index.js";
17+
export { LightPushCodec, wakuLightPush } from "./lib/light_push/index.js";
1918

2019
export * as waku_store from "./lib/store/index.js";
2120

packages/core/src/lib/base_protocol.ts

+29-12
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type {
99
import { DefaultPubsubTopic } from "@waku/interfaces";
1010
import { Logger, shardInfoToPubsubTopics } from "@waku/utils";
1111
import {
12-
getConnectedPeersForProtocol,
12+
getConnectedPeersForProtocolAndShard,
1313
getPeersForProtocol,
1414
sortPeersByLatency
1515
} from "@waku/utils/libp2p";
@@ -25,12 +25,16 @@ export class BaseProtocol implements IBaseProtocol {
2525
public readonly addLibp2pEventListener: Libp2p["addEventListener"];
2626
public readonly removeLibp2pEventListener: Libp2p["removeEventListener"];
2727
protected streamManager: StreamManager;
28+
protected pubsubTopics: PubsubTopic[];
2829

2930
constructor(
3031
public multicodec: string,
3132
private components: Libp2pComponents,
32-
private log: Logger
33+
private log: Logger,
34+
private options?: ProtocolCreateOptions
3335
) {
36+
this.pubsubTopics = this.initializePubsubTopic(options);
37+
3438
this.addLibp2pEventListener = components.events.addEventListener.bind(
3539
components.events
3640
);
@@ -59,10 +63,19 @@ export class BaseProtocol implements IBaseProtocol {
5963
* the class protocol. Waku may or may not be currently connected to these
6064
* peers.
6165
*/
62-
public async peers(): Promise<Peer[]> {
66+
public async allPeers(): Promise<Peer[]> {
6367
return getPeersForProtocol(this.peerStore, [this.multicodec]);
6468
}
6569

70+
public async connectedPeers(): Promise<Peer[]> {
71+
const peers = await this.allPeers();
72+
return peers.filter((peer) => {
73+
return (
74+
this.components.connectionManager.getConnections(peer.id).length > 0
75+
);
76+
});
77+
}
78+
6679
/**
6780
* Retrieves a list of connected peers that support the protocol. The list is sorted by latency.
6881
*
@@ -83,16 +96,18 @@ export class BaseProtocol implements IBaseProtocol {
8396
numPeers: 0
8497
}
8598
): Promise<Peer[]> {
86-
// Retrieve all connected peers that support the protocol
87-
const allPeersForProtocol = await getConnectedPeersForProtocol(
88-
this.components.connectionManager.getConnections(),
89-
this.peerStore,
90-
[this.multicodec]
91-
);
99+
// Retrieve all connected peers that support the protocol & shard (if configured)
100+
const connectedPeersForProtocolAndShard =
101+
await getConnectedPeersForProtocolAndShard(
102+
this.components.connectionManager.getConnections(),
103+
this.peerStore,
104+
[this.multicodec],
105+
this.options?.shardInfo
106+
);
92107

93108
// Filter the peers based on discovery & number of peers requested
94-
const filteredPeers = await filterPeersByDiscovery(
95-
allPeersForProtocol,
109+
const filteredPeers = filterPeersByDiscovery(
110+
connectedPeersForProtocolAndShard,
96111
numPeers,
97112
maxBootstrapPeers
98113
);
@@ -112,7 +127,9 @@ export class BaseProtocol implements IBaseProtocol {
112127
return sortedFilteredPeers;
113128
}
114129

115-
initializePubsubTopic(options?: ProtocolCreateOptions): PubsubTopic[] {
130+
private initializePubsubTopic(
131+
options?: ProtocolCreateOptions
132+
): PubsubTopic[] {
116133
return (
117134
options?.pubsubTopics ??
118135
(options?.shardInfo

packages/core/src/lib/connection_manager.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { Peer, PeerId, PeerInfo, PeerStore } from "@libp2p/interface";
22
import { CustomEvent, TypedEventEmitter } from "@libp2p/interface";
3-
import { decodeRelayShard } from "@waku/enr";
43
import {
54
ConnectionManagerOptions,
65
EConnectionStateEvents,
@@ -15,7 +14,7 @@ import {
1514
ShardInfo
1615
} from "@waku/interfaces";
1716
import { Libp2p, Tags } from "@waku/interfaces";
18-
import { shardInfoToPubsubTopics } from "@waku/utils";
17+
import { decodeRelayShard, shardInfoToPubsubTopics } from "@waku/utils";
1918
import { Logger } from "@waku/utils";
2019

2120
import { KeepAliveManager } from "./keep_alive_manager.js";
@@ -377,6 +376,8 @@ export class ConnectionManager
377376
},
378377
"peer:connect": (evt: CustomEvent<PeerId>): void => {
379378
void (async () => {
379+
log.info(`Connected to peer ${evt.detail.toString()}`);
380+
380381
const peerId = evt.detail;
381382

382383
this.keepAliveManager.start(

packages/core/src/lib/filter/index.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,6 @@ class Subscription {
258258
}
259259

260260
class Filter extends BaseProtocol implements IReceiver {
261-
private readonly pubsubTopics: PubsubTopic[] = [];
262261
private activeSubscriptions = new Map<string, Subscription>();
263262
private readonly NUM_PEERS_PROTOCOL = 1;
264263

@@ -279,9 +278,7 @@ class Filter extends BaseProtocol implements IReceiver {
279278
}
280279

281280
constructor(libp2p: Libp2p, options?: ProtocolCreateOptions) {
282-
super(FilterCodecs.SUBSCRIBE, libp2p.components, log);
283-
284-
this.pubsubTopics = this.initializePubsubTopic(options);
281+
super(FilterCodecs.SUBSCRIBE, libp2p.components, log, options);
285282

286283
libp2p.handle(FilterCodecs.PUSH, this.onRequest.bind(this)).catch((e) => {
287284
log.error("Failed to register ", FilterCodecs.PUSH, e);
@@ -300,8 +297,6 @@ class Filter extends BaseProtocol implements IReceiver {
300297

301298
ensurePubsubTopicIsConfigured(pubsubTopic, this.pubsubTopics);
302299

303-
//TODO: get a relevant peer for the topic/shard
304-
// https://github.com/waku-org/js-waku/pull/1586#discussion_r1336428230
305300
const peer = (
306301
await this.getPeers({
307302
maxBootstrapPeers: 1,

packages/core/src/lib/filterPeers.spec.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ describe("filterPeersByDiscovery function", function () {
2727
}
2828
] as unknown as Peer[];
2929

30-
const result = await filterPeersByDiscovery(mockPeers, 0, 10);
30+
const result = filterPeersByDiscovery(mockPeers, 0, 10);
3131
expect(result.length).to.deep.equal(mockPeers.length);
3232
});
3333

@@ -56,7 +56,7 @@ describe("filterPeersByDiscovery function", function () {
5656
}
5757
] as unknown as Peer[];
5858

59-
const result = await filterPeersByDiscovery(mockPeers, 0, 0);
59+
const result = filterPeersByDiscovery(mockPeers, 0, 0);
6060

6161
// result should have no bootstrap peers, and a total of 2 peers
6262
expect(result.length).to.equal(2);
@@ -95,7 +95,7 @@ describe("filterPeersByDiscovery function", function () {
9595
}
9696
] as unknown as Peer[];
9797

98-
const result = await filterPeersByDiscovery(mockPeers, 0, 1);
98+
const result = filterPeersByDiscovery(mockPeers, 0, 1);
9999

100100
// result should have 1 bootstrap peers, and a total of 4 peers
101101
expect(result.length).to.equal(4);
@@ -134,7 +134,7 @@ describe("filterPeersByDiscovery function", function () {
134134
}
135135
] as unknown as Peer[];
136136

137-
const result = await filterPeersByDiscovery(mockPeers, 5, 2);
137+
const result = filterPeersByDiscovery(mockPeers, 5, 2);
138138

139139
// check that result has at least 2 bootstrap peers and no more than 5 peers
140140
expect(result.length).to.be.at.least(2);

packages/core/src/lib/filterPeers.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ import { Tags } from "@waku/interfaces";
1010
* @param peers - The list of peers to filter from.
1111
* @param numPeers - The total number of peers to retrieve. If 0, all peers are returned, irrespective of `maxBootstrapPeers`.
1212
* @param maxBootstrapPeers - The maximum number of bootstrap peers to retrieve.
13-
* @returns A Promise that resolves to an array of peers based on the specified criteria.
13+
* @returns An array of peers based on the specified criteria.
1414
*/
15-
export async function filterPeersByDiscovery(
15+
export function filterPeersByDiscovery(
1616
peers: Peer[],
1717
numPeers: number,
1818
maxBootstrapPeers: number
19-
): Promise<Peer[]> {
19+
): Peer[] {
2020
// Collect the bootstrap peers up to the specified maximum
2121
let bootstrapPeers = peers
2222
.filter((peer) => peer.tags.has(Tags.BOOTSTRAP))

packages/core/src/lib/light_push/index.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
IMessage,
66
Libp2p,
77
ProtocolCreateOptions,
8-
PubsubTopic,
98
SendError,
109
SendResult
1110
} from "@waku/interfaces";
@@ -43,12 +42,10 @@ type PreparePushMessageResult =
4342
* Implements the [Waku v2 Light Push protocol](https://rfc.vac.dev/spec/19/).
4443
*/
4544
class LightPush extends BaseProtocol implements ILightPush {
46-
private readonly pubsubTopics: PubsubTopic[];
4745
private readonly NUM_PEERS_PROTOCOL = 1;
4846

4947
constructor(libp2p: Libp2p, options?: ProtocolCreateOptions) {
50-
super(LightPushCodec, libp2p.components, log);
51-
this.pubsubTopics = this.initializePubsubTopic(options);
48+
super(LightPushCodec, libp2p.components, log, options);
5249
}
5350

5451
private async preparePushMessage(
@@ -107,7 +104,6 @@ class LightPush extends BaseProtocol implements ILightPush {
107104
};
108105
}
109106

110-
//TODO: get a relevant peer for the topic/shard
111107
const peers = await this.getPeers({
112108
maxBootstrapPeers: 1,
113109
numPeers: this.NUM_PEERS_PROTOCOL

packages/core/src/lib/metadata/index.ts

+38-13
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import type { PeerId } from "@libp2p/interface";
22
import { IncomingStreamData } from "@libp2p/interface";
3-
import { encodeRelayShard } from "@waku/enr";
43
import type {
54
IMetadata,
65
Libp2pComponents,
6+
PeerIdStr,
77
ShardInfo,
88
ShardingParams
99
} from "@waku/interfaces";
1010
import { proto_metadata } from "@waku/proto";
11-
import { Logger } from "@waku/utils";
11+
import { encodeRelayShard, Logger } from "@waku/utils";
1212
import all from "it-all";
1313
import * as lp from "it-length-prefixed";
1414
import { pipe } from "it-pipe";
@@ -20,13 +20,16 @@ const log = new Logger("metadata");
2020

2121
export const MetadataCodec = "/vac/waku/metadata/1.0.0";
2222

23-
class Metadata extends BaseProtocol {
24-
private readonly shardInfo: ShardingParams;
23+
class Metadata extends BaseProtocol implements IMetadata {
2524
private libp2pComponents: Libp2pComponents;
26-
constructor(shardInfo: ShardingParams, libp2p: Libp2pComponents) {
27-
super(MetadataCodec, libp2p.components, log);
25+
handshakesConfirmed: Set<PeerIdStr> = new Set();
26+
27+
constructor(
28+
public shardInfo: ShardingParams,
29+
libp2p: Libp2pComponents
30+
) {
31+
super(MetadataCodec, libp2p.components, log, shardInfo && { shardInfo });
2832
this.libp2pComponents = libp2p;
29-
this.shardInfo = shardInfo;
3033
void libp2p.registrar.handle(MetadataCodec, (streamData) => {
3134
void this.onRequest(streamData);
3235
});
@@ -53,12 +56,10 @@ class Metadata extends BaseProtocol {
5356
const remoteShardInfoResponse =
5457
this.decodeMetadataResponse(encodedResponse);
5558

56-
// add or update the shardInfo to peer store
57-
await this.libp2pComponents.peerStore.merge(connection.remotePeer, {
58-
metadata: {
59-
shardInfo: encodeRelayShard(remoteShardInfoResponse)
60-
}
61-
});
59+
await this.savePeerShardInfo(
60+
connection.remotePeer,
61+
remoteShardInfoResponse
62+
);
6263
} catch (error) {
6364
log.error("Error handling metadata request", error);
6465
}
@@ -87,9 +88,19 @@ class Metadata extends BaseProtocol {
8788

8889
const decodedResponse = this.decodeMetadataResponse(encodedResponse);
8990

91+
await this.savePeerShardInfo(peerId, decodedResponse);
92+
9093
return decodedResponse;
9194
}
9295

96+
public async confirmOrAttemptHandshake(peerId: PeerId): Promise<void> {
97+
if (this.handshakesConfirmed.has(peerId.toString())) return;
98+
99+
await this.query(peerId);
100+
101+
return;
102+
}
103+
93104
private decodeMetadataResponse(encodedResponse: Uint8ArrayList[]): ShardInfo {
94105
const bytes = new Uint8ArrayList();
95106

@@ -104,6 +115,20 @@ class Metadata extends BaseProtocol {
104115

105116
return response;
106117
}
118+
119+
private async savePeerShardInfo(
120+
peerId: PeerId,
121+
shardInfo: ShardInfo
122+
): Promise<void> {
123+
// add or update the shardInfo to peer store
124+
await this.libp2pComponents.peerStore.merge(peerId, {
125+
metadata: {
126+
shardInfo: encodeRelayShard(shardInfo)
127+
}
128+
});
129+
130+
this.handshakesConfirmed.add(peerId.toString());
131+
}
107132
}
108133

109134
export function wakuMetadata(

packages/core/src/lib/store/index.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ import {
66
IDecoder,
77
IStore,
88
Libp2p,
9-
ProtocolCreateOptions,
10-
PubsubTopic
9+
ProtocolCreateOptions
1110
} from "@waku/interfaces";
1211
import { proto_store as proto } from "@waku/proto";
1312
import { ensurePubsubTopicIsConfigured, isDefined } from "@waku/utils";
@@ -74,12 +73,10 @@ export interface QueryOptions {
7473
* The Waku Store protocol can be used to retrieved historical messages.
7574
*/
7675
class Store extends BaseProtocol implements IStore {
77-
private readonly pubsubTopics: PubsubTopic[];
7876
private readonly NUM_PEERS_PROTOCOL = 1;
7977

8078
constructor(libp2p: Libp2p, options?: ProtocolCreateOptions) {
81-
super(StoreCodec, libp2p.components, log);
82-
this.pubsubTopics = this.initializePubsubTopic(options);
79+
super(StoreCodec, libp2p.components, log, options);
8380
}
8481

8582
/**

0 commit comments

Comments
 (0)