@waku/core 0.0.26-7eb3375.0 → 0.0.27
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 +56 -0
- package/bundle/base_protocol-LhsIWF3-.js +308 -0
- package/bundle/{browser-9a6558bb.js → browser-BQyFvtq6.js} +579 -699
- package/bundle/index-8YyfzF9R.js +650 -0
- package/bundle/index.js +3076 -21759
- package/bundle/lib/base_protocol.js +3 -3
- package/bundle/lib/message/version_0.js +3 -3
- package/bundle/lib/predefined_bootstrap_nodes.js +1 -1
- package/bundle/{version_0-7190df43.js → version_0-FXfzO8Km.js} +1368 -2459
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.d.ts +2 -5
- package/dist/index.js +2 -5
- package/dist/index.js.map +1 -1
- package/dist/lib/base_protocol.d.ts +14 -10
- package/dist/lib/base_protocol.js +38 -16
- package/dist/lib/base_protocol.js.map +1 -1
- package/dist/lib/connection_manager.d.ts +2 -2
- package/dist/lib/connection_manager.js +16 -6
- package/dist/lib/connection_manager.js.map +1 -1
- package/dist/lib/filter/index.d.ts +1 -1
- package/dist/lib/filter/index.js +146 -85
- package/dist/lib/filter/index.js.map +1 -1
- package/dist/lib/filterPeers.d.ts +8 -5
- package/dist/lib/filterPeers.js +12 -5
- package/dist/lib/filterPeers.js.map +1 -1
- package/dist/lib/keep_alive_manager.d.ts +2 -3
- package/dist/lib/keep_alive_manager.js +2 -2
- package/dist/lib/keep_alive_manager.js.map +1 -1
- package/dist/lib/light_push/index.js +3 -7
- package/dist/lib/light_push/index.js.map +1 -1
- package/dist/lib/message/version_0.d.ts +3 -3
- package/dist/lib/message/version_0.js +5 -6
- package/dist/lib/message/version_0.js.map +1 -1
- package/dist/lib/metadata/index.d.ts +3 -0
- package/dist/lib/metadata/index.js +81 -0
- package/dist/lib/metadata/index.js.map +1 -0
- package/dist/lib/predefined_bootstrap_nodes.js.map +1 -1
- package/dist/lib/store/history_rpc.js.map +1 -1
- package/dist/lib/store/index.js +1 -4
- package/dist/lib/store/index.js.map +1 -1
- package/dist/lib/stream_manager.d.ts +2 -2
- package/dist/lib/stream_manager.js.map +1 -1
- package/dist/lib/wait_for_remote_peer.d.ts +1 -1
- package/dist/lib/wait_for_remote_peer.js +40 -10
- package/dist/lib/wait_for_remote_peer.js.map +1 -1
- package/package.json +129 -1
- package/src/index.ts +3 -6
- package/src/lib/base_protocol.ts +71 -24
- package/src/lib/connection_manager.ts +17 -10
- package/src/lib/filter/index.ts +237 -139
- package/src/lib/filterPeers.ts +15 -7
- package/src/lib/keep_alive_manager.ts +4 -5
- package/src/lib/light_push/index.ts +11 -11
- package/src/lib/message/version_0.ts +16 -8
- package/src/lib/metadata/index.ts +142 -0
- package/src/lib/store/index.ts +3 -7
- package/src/lib/stream_manager.ts +2 -3
- package/src/lib/wait_for_remote_peer.ts +58 -12
- package/bundle/base_protocol-46017f51.js +0 -468
- package/bundle/index-7581d519.js +0 -31
- package/dist/lib/constants.d.ts +0 -4
- package/dist/lib/constants.js +0 -5
- package/dist/lib/constants.js.map +0 -1
- package/dist/lib/waku.d.ts +0 -57
- package/dist/lib/waku.js +0 -124
- package/dist/lib/waku.js.map +0 -1
- package/src/lib/constants.ts +0 -4
- package/src/lib/waku.ts +0 -204
package/src/lib/filter/index.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
import { Stream } from "@libp2p/interface
|
2
|
-
import type { Peer } from "@libp2p/interface
|
3
|
-
import type { IncomingStreamData } from "@libp2p/interface-internal
|
1
|
+
import { Stream } from "@libp2p/interface";
|
2
|
+
import type { Peer } from "@libp2p/interface";
|
3
|
+
import type { IncomingStreamData } from "@libp2p/interface-internal";
|
4
4
|
import type {
|
5
5
|
Callback,
|
6
6
|
ContentTopic,
|
@@ -11,15 +11,18 @@ import type {
|
|
11
11
|
IProtoMessage,
|
12
12
|
IReceiver,
|
13
13
|
Libp2p,
|
14
|
-
PeerIdStr,
|
15
14
|
ProtocolCreateOptions,
|
16
15
|
PubsubTopic,
|
16
|
+
SingleShardInfo,
|
17
17
|
Unsubscribe
|
18
18
|
} from "@waku/interfaces";
|
19
|
+
import { DefaultPubsubTopic } from "@waku/interfaces";
|
20
|
+
import { messageHashStr } from "@waku/message-hash";
|
19
21
|
import { WakuMessage } from "@waku/proto";
|
20
22
|
import {
|
21
23
|
ensurePubsubTopicIsConfigured,
|
22
24
|
groupByContentTopic,
|
25
|
+
singleShardInfoToPubsubTopic,
|
23
26
|
toAsyncIterator
|
24
27
|
} from "@waku/utils";
|
25
28
|
import { Logger } from "@waku/utils";
|
@@ -28,7 +31,6 @@ import * as lp from "it-length-prefixed";
|
|
28
31
|
import { pipe } from "it-pipe";
|
29
32
|
|
30
33
|
import { BaseProtocol } from "../base_protocol.js";
|
31
|
-
import { DefaultPubsubTopic } from "../constants.js";
|
32
34
|
|
33
35
|
import {
|
34
36
|
FilterPushRpc,
|
@@ -48,10 +50,14 @@ export const FilterCodecs = {
|
|
48
50
|
PUSH: "/vac/waku/filter-push/2.0.0-beta1"
|
49
51
|
};
|
50
52
|
|
53
|
+
/**
|
54
|
+
* A subscription object refers to a subscription to a given pubsub topic.
|
55
|
+
*/
|
51
56
|
class Subscription {
|
52
|
-
|
57
|
+
readonly peers: Peer[];
|
53
58
|
private readonly pubsubTopic: PubsubTopic;
|
54
59
|
private newStream: (peer: Peer) => Promise<Stream>;
|
60
|
+
readonly receivedMessagesHashStr: string[] = [];
|
55
61
|
|
56
62
|
private subscriptionCallbacks: Map<
|
57
63
|
ContentTopic,
|
@@ -60,10 +66,10 @@ class Subscription {
|
|
60
66
|
|
61
67
|
constructor(
|
62
68
|
pubsubTopic: PubsubTopic,
|
63
|
-
|
69
|
+
remotePeers: Peer[],
|
64
70
|
newStream: (peer: Peer) => Promise<Stream>
|
65
71
|
) {
|
66
|
-
this.
|
72
|
+
this.peers = remotePeers;
|
67
73
|
this.pubsubTopic = pubsubTopic;
|
68
74
|
this.newStream = newStream;
|
69
75
|
this.subscriptionCallbacks = new Map();
|
@@ -87,53 +93,59 @@ class Subscription {
|
|
87
93
|
const decodersGroupedByCT = groupByContentTopic(decodersArray);
|
88
94
|
const contentTopics = Array.from(decodersGroupedByCT.keys());
|
89
95
|
|
90
|
-
const
|
96
|
+
const promises = this.peers.map(async (peer) => {
|
97
|
+
const stream = await this.newStream(peer);
|
91
98
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
);
|
96
|
-
|
97
|
-
try {
|
98
|
-
const res = await pipe(
|
99
|
-
[request.encode()],
|
100
|
-
lp.encode,
|
101
|
-
stream,
|
102
|
-
lp.decode,
|
103
|
-
async (source) => await all(source)
|
99
|
+
const request = FilterSubscribeRpc.createSubscribeRequest(
|
100
|
+
this.pubsubTopic,
|
101
|
+
contentTopics
|
104
102
|
);
|
105
103
|
|
106
|
-
|
107
|
-
|
108
|
-
|
104
|
+
try {
|
105
|
+
const res = await pipe(
|
106
|
+
[request.encode()],
|
107
|
+
lp.encode,
|
108
|
+
stream,
|
109
|
+
lp.decode,
|
110
|
+
async (source) => await all(source)
|
109
111
|
);
|
110
|
-
}
|
111
112
|
|
112
|
-
|
113
|
-
|
113
|
+
if (!res || !res.length) {
|
114
|
+
throw Error(
|
115
|
+
`No response received for request ${request.requestId}: ${res}`
|
116
|
+
);
|
117
|
+
}
|
118
|
+
|
119
|
+
const { statusCode, requestId, statusDesc } =
|
120
|
+
FilterSubscribeResponse.decode(res[0].slice());
|
121
|
+
|
122
|
+
if (statusCode < 200 || statusCode >= 300) {
|
123
|
+
throw new Error(
|
124
|
+
`Filter subscribe request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
|
125
|
+
);
|
126
|
+
}
|
114
127
|
|
115
|
-
|
128
|
+
log.info(
|
129
|
+
"Subscribed to peer ",
|
130
|
+
peer.id.toString(),
|
131
|
+
"for content topics",
|
132
|
+
contentTopics
|
133
|
+
);
|
134
|
+
} catch (e) {
|
116
135
|
throw new Error(
|
117
|
-
|
136
|
+
"Error subscribing to peer: " +
|
137
|
+
peer.id.toString() +
|
138
|
+
" for content topics: " +
|
139
|
+
contentTopics +
|
140
|
+
": " +
|
141
|
+
e
|
118
142
|
);
|
119
143
|
}
|
144
|
+
});
|
120
145
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
"for content topics",
|
125
|
-
contentTopics
|
126
|
-
);
|
127
|
-
} catch (e) {
|
128
|
-
throw new Error(
|
129
|
-
"Error subscribing to peer: " +
|
130
|
-
this.peer.id.toString() +
|
131
|
-
" for content topics: " +
|
132
|
-
contentTopics +
|
133
|
-
": " +
|
134
|
-
e
|
135
|
-
);
|
136
|
-
}
|
146
|
+
const results = await Promise.allSettled(promises);
|
147
|
+
|
148
|
+
this.handleErrors(results, "subscribe");
|
137
149
|
|
138
150
|
// Save the callback functions by content topics so they
|
139
151
|
// can easily be removed (reciprocally replaced) if `unsubscribe` (reciprocally `subscribe`)
|
@@ -153,133 +165,206 @@ class Subscription {
|
|
153
165
|
}
|
154
166
|
|
155
167
|
async unsubscribe(contentTopics: ContentTopic[]): Promise<void> {
|
156
|
-
const
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
168
|
+
const promises = this.peers.map(async (peer) => {
|
169
|
+
const stream = await this.newStream(peer);
|
170
|
+
const unsubscribeRequest = FilterSubscribeRpc.createUnsubscribeRequest(
|
171
|
+
this.pubsubTopic,
|
172
|
+
contentTopics
|
173
|
+
);
|
161
174
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
175
|
+
try {
|
176
|
+
await pipe([unsubscribeRequest.encode()], lp.encode, stream.sink);
|
177
|
+
} catch (error) {
|
178
|
+
throw new Error("Error unsubscribing: " + error);
|
179
|
+
}
|
167
180
|
|
168
|
-
|
169
|
-
|
181
|
+
contentTopics.forEach((contentTopic: string) => {
|
182
|
+
this.subscriptionCallbacks.delete(contentTopic);
|
183
|
+
});
|
170
184
|
});
|
185
|
+
|
186
|
+
const results = await Promise.allSettled(promises);
|
187
|
+
|
188
|
+
this.handleErrors(results, "unsubscribe");
|
171
189
|
}
|
172
190
|
|
173
191
|
async ping(): Promise<void> {
|
174
|
-
const
|
192
|
+
const promises = this.peers.map(async (peer) => {
|
193
|
+
const stream = await this.newStream(peer);
|
194
|
+
|
195
|
+
const request = FilterSubscribeRpc.createSubscriberPingRequest();
|
196
|
+
|
197
|
+
try {
|
198
|
+
const res = await pipe(
|
199
|
+
[request.encode()],
|
200
|
+
lp.encode,
|
201
|
+
stream,
|
202
|
+
lp.decode,
|
203
|
+
async (source) => await all(source)
|
204
|
+
);
|
175
205
|
|
176
|
-
|
206
|
+
if (!res || !res.length) {
|
207
|
+
throw Error(
|
208
|
+
`No response received for request ${request.requestId}: ${res}`
|
209
|
+
);
|
210
|
+
}
|
177
211
|
|
178
|
-
|
179
|
-
|
180
|
-
[request.encode()],
|
181
|
-
lp.encode,
|
182
|
-
stream,
|
183
|
-
lp.decode,
|
184
|
-
async (source) => await all(source)
|
185
|
-
);
|
212
|
+
const { statusCode, requestId, statusDesc } =
|
213
|
+
FilterSubscribeResponse.decode(res[0].slice());
|
186
214
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
215
|
+
if (statusCode < 200 || statusCode >= 300) {
|
216
|
+
throw new Error(
|
217
|
+
`Filter ping request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
|
218
|
+
);
|
219
|
+
}
|
220
|
+
log.info(`Ping successful for peer ${peer.id.toString()}`);
|
221
|
+
} catch (error) {
|
222
|
+
log.error("Error pinging: ", error);
|
223
|
+
throw error; // Rethrow the actual error instead of wrapping it
|
191
224
|
}
|
225
|
+
});
|
192
226
|
|
193
|
-
|
194
|
-
FilterSubscribeResponse.decode(res[0].slice());
|
195
|
-
|
196
|
-
if (statusCode < 200 || statusCode >= 300) {
|
197
|
-
throw new Error(
|
198
|
-
`Filter ping request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
|
199
|
-
);
|
200
|
-
}
|
227
|
+
const results = await Promise.allSettled(promises);
|
201
228
|
|
202
|
-
|
203
|
-
} catch (error) {
|
204
|
-
log.error("Error pinging: ", error);
|
205
|
-
throw new Error("Error pinging: " + error);
|
206
|
-
}
|
229
|
+
this.handleErrors(results, "ping");
|
207
230
|
}
|
208
231
|
|
209
232
|
async unsubscribeAll(): Promise<void> {
|
210
|
-
const
|
211
|
-
|
212
|
-
const request = FilterSubscribeRpc.createUnsubscribeAllRequest(
|
213
|
-
this.pubsubTopic
|
214
|
-
);
|
233
|
+
const promises = this.peers.map(async (peer) => {
|
234
|
+
const stream = await this.newStream(peer);
|
215
235
|
|
216
|
-
|
217
|
-
|
218
|
-
[request.encode()],
|
219
|
-
lp.encode,
|
220
|
-
stream,
|
221
|
-
lp.decode,
|
222
|
-
async (source) => await all(source)
|
236
|
+
const request = FilterSubscribeRpc.createUnsubscribeAllRequest(
|
237
|
+
this.pubsubTopic
|
223
238
|
);
|
224
239
|
|
225
|
-
|
226
|
-
|
227
|
-
|
240
|
+
try {
|
241
|
+
const res = await pipe(
|
242
|
+
[request.encode()],
|
243
|
+
lp.encode,
|
244
|
+
stream,
|
245
|
+
lp.decode,
|
246
|
+
async (source) => await all(source)
|
228
247
|
);
|
229
|
-
}
|
230
248
|
|
231
|
-
|
232
|
-
|
249
|
+
if (!res || !res.length) {
|
250
|
+
throw Error(
|
251
|
+
`No response received for request ${request.requestId}: ${res}`
|
252
|
+
);
|
253
|
+
}
|
254
|
+
|
255
|
+
const { statusCode, requestId, statusDesc } =
|
256
|
+
FilterSubscribeResponse.decode(res[0].slice());
|
233
257
|
|
234
|
-
|
258
|
+
if (statusCode < 200 || statusCode >= 300) {
|
259
|
+
throw new Error(
|
260
|
+
`Filter unsubscribe all request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
|
261
|
+
);
|
262
|
+
}
|
263
|
+
|
264
|
+
this.subscriptionCallbacks.clear();
|
265
|
+
log.info(
|
266
|
+
`Unsubscribed from all content topics for pubsub topic ${this.pubsubTopic}`
|
267
|
+
);
|
268
|
+
} catch (error) {
|
235
269
|
throw new Error(
|
236
|
-
|
270
|
+
"Error unsubscribing from all content topics: " + error
|
237
271
|
);
|
238
272
|
}
|
273
|
+
});
|
239
274
|
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
throw new Error("Error unsubscribing from all content topics: " + error);
|
244
|
-
}
|
275
|
+
const results = await Promise.allSettled(promises);
|
276
|
+
|
277
|
+
this.handleErrors(results, "unsubscribeAll");
|
245
278
|
}
|
246
279
|
|
247
280
|
async processMessage(message: WakuMessage): Promise<void> {
|
248
|
-
const
|
281
|
+
const hashedMessageStr = messageHashStr(
|
282
|
+
this.pubsubTopic,
|
283
|
+
message as IProtoMessage
|
284
|
+
);
|
285
|
+
if (this.receivedMessagesHashStr.includes(hashedMessageStr)) {
|
286
|
+
log.info("Message already received, skipping");
|
287
|
+
return;
|
288
|
+
}
|
289
|
+
this.receivedMessagesHashStr.push(hashedMessageStr);
|
290
|
+
|
291
|
+
const { contentTopic } = message;
|
249
292
|
const subscriptionCallback = this.subscriptionCallbacks.get(contentTopic);
|
250
293
|
if (!subscriptionCallback) {
|
251
294
|
log.error("No subscription callback available for ", contentTopic);
|
252
295
|
return;
|
253
296
|
}
|
297
|
+
log.info(
|
298
|
+
"Processing message with content topic ",
|
299
|
+
contentTopic,
|
300
|
+
" on pubsub topic ",
|
301
|
+
this.pubsubTopic
|
302
|
+
);
|
254
303
|
await pushMessage(subscriptionCallback, this.pubsubTopic, message);
|
255
304
|
}
|
305
|
+
|
306
|
+
// Filter out only the rejected promises and extract & handle their reasons
|
307
|
+
private handleErrors(
|
308
|
+
results: PromiseSettledResult<void>[],
|
309
|
+
type: "ping" | "subscribe" | "unsubscribe" | "unsubscribeAll"
|
310
|
+
): void {
|
311
|
+
const errors = results
|
312
|
+
.filter(
|
313
|
+
(result): result is PromiseRejectedResult =>
|
314
|
+
result.status === "rejected"
|
315
|
+
)
|
316
|
+
.map((rejectedResult) => rejectedResult.reason);
|
317
|
+
|
318
|
+
if (errors.length === this.peers.length) {
|
319
|
+
const errorCounts = new Map<string, number>();
|
320
|
+
// TODO: streamline error logging with https://github.com/orgs/waku-org/projects/2/views/1?pane=issue&itemId=42849952
|
321
|
+
errors.forEach((error) => {
|
322
|
+
const message = error instanceof Error ? error.message : String(error);
|
323
|
+
errorCounts.set(message, (errorCounts.get(message) || 0) + 1);
|
324
|
+
});
|
325
|
+
|
326
|
+
const uniqueErrorMessages = Array.from(
|
327
|
+
errorCounts,
|
328
|
+
([message, count]) => `${message} (occurred ${count} times)`
|
329
|
+
).join(", ");
|
330
|
+
throw new Error(`Error ${type} all peers: ${uniqueErrorMessages}`);
|
331
|
+
} else if (errors.length > 0) {
|
332
|
+
// TODO: handle renewing faulty peers with new peers (https://github.com/waku-org/js-waku/issues/1463)
|
333
|
+
log.warn(
|
334
|
+
`Some ${type} failed. These will be refreshed with new peers`,
|
335
|
+
errors
|
336
|
+
);
|
337
|
+
} else {
|
338
|
+
log.info(`${type} successful for all peers`);
|
339
|
+
}
|
340
|
+
}
|
256
341
|
}
|
257
342
|
|
258
343
|
class Filter extends BaseProtocol implements IReceiver {
|
259
|
-
private readonly pubsubTopics: PubsubTopic[] = [];
|
260
344
|
private activeSubscriptions = new Map<string, Subscription>();
|
261
|
-
private readonly NUM_PEERS_PROTOCOL = 1;
|
262
345
|
|
263
346
|
private getActiveSubscription(
|
264
|
-
pubsubTopic: PubsubTopic
|
265
|
-
peerIdStr: PeerIdStr
|
347
|
+
pubsubTopic: PubsubTopic
|
266
348
|
): Subscription | undefined {
|
267
|
-
return this.activeSubscriptions.get(
|
349
|
+
return this.activeSubscriptions.get(pubsubTopic);
|
268
350
|
}
|
269
351
|
|
270
352
|
private setActiveSubscription(
|
271
353
|
pubsubTopic: PubsubTopic,
|
272
|
-
peerIdStr: PeerIdStr,
|
273
354
|
subscription: Subscription
|
274
355
|
): Subscription {
|
275
|
-
this.activeSubscriptions.set(
|
356
|
+
this.activeSubscriptions.set(pubsubTopic, subscription);
|
276
357
|
return subscription;
|
277
358
|
}
|
278
359
|
|
279
360
|
constructor(libp2p: Libp2p, options?: ProtocolCreateOptions) {
|
280
|
-
super(
|
281
|
-
|
282
|
-
|
361
|
+
super(
|
362
|
+
FilterCodecs.SUBSCRIBE,
|
363
|
+
libp2p.components,
|
364
|
+
log,
|
365
|
+
options!.pubsubTopics!,
|
366
|
+
options
|
367
|
+
);
|
283
368
|
|
284
369
|
libp2p.handle(FilterCodecs.PUSH, this.onRequest.bind(this)).catch((e) => {
|
285
370
|
log.error("Failed to register ", FilterCodecs.PUSH, e);
|
@@ -288,26 +373,40 @@ class Filter extends BaseProtocol implements IReceiver {
|
|
288
373
|
this.activeSubscriptions = new Map();
|
289
374
|
}
|
290
375
|
|
376
|
+
/**
|
377
|
+
* Creates a new subscription to the given pubsub topic.
|
378
|
+
* The subscription is made to multiple peers for decentralization.
|
379
|
+
* @param pubsubTopicShardInfo The pubsub topic to subscribe to.
|
380
|
+
* @returns The subscription object.
|
381
|
+
*/
|
291
382
|
async createSubscription(
|
292
|
-
|
383
|
+
pubsubTopicShardInfo: SingleShardInfo | PubsubTopic = DefaultPubsubTopic
|
293
384
|
): Promise<Subscription> {
|
385
|
+
const pubsubTopic =
|
386
|
+
typeof pubsubTopicShardInfo == "string"
|
387
|
+
? pubsubTopicShardInfo
|
388
|
+
: singleShardInfoToPubsubTopic(pubsubTopicShardInfo);
|
389
|
+
|
294
390
|
ensurePubsubTopicIsConfigured(pubsubTopic, this.pubsubTopics);
|
295
391
|
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
392
|
+
const peers = await this.getPeers({
|
393
|
+
maxBootstrapPeers: 1,
|
394
|
+
numPeers: this.numPeersToUse
|
395
|
+
});
|
396
|
+
if (peers.length === 0) {
|
397
|
+
throw new Error("No peer found to initiate subscription.");
|
398
|
+
}
|
399
|
+
|
400
|
+
log.info(
|
401
|
+
`Creating filter subscription with ${peers.length} peers: `,
|
402
|
+
peers.map((peer) => peer.id.toString())
|
403
|
+
);
|
304
404
|
|
305
405
|
const subscription =
|
306
|
-
this.getActiveSubscription(pubsubTopic
|
406
|
+
this.getActiveSubscription(pubsubTopic) ??
|
307
407
|
this.setActiveSubscription(
|
308
408
|
pubsubTopic,
|
309
|
-
|
310
|
-
new Subscription(pubsubTopic, peer, this.getStream.bind(this, peer))
|
409
|
+
new Subscription(pubsubTopic, peers, this.getStream.bind(this))
|
311
410
|
);
|
312
411
|
|
313
412
|
return subscription;
|
@@ -354,8 +453,11 @@ class Filter extends BaseProtocol implements IReceiver {
|
|
354
453
|
}
|
355
454
|
|
356
455
|
private onRequest(streamData: IncomingStreamData): void {
|
456
|
+
const { connection, stream } = streamData;
|
457
|
+
const { remotePeer } = connection;
|
458
|
+
log.info(`Received message from ${remotePeer.toString()}`);
|
357
459
|
try {
|
358
|
-
pipe(
|
460
|
+
pipe(stream, lp.decode, async (source) => {
|
359
461
|
for await (const bytes of source) {
|
360
462
|
const response = FilterPushRpc.decode(bytes.slice());
|
361
463
|
|
@@ -371,11 +473,7 @@ class Filter extends BaseProtocol implements IReceiver {
|
|
371
473
|
return;
|
372
474
|
}
|
373
475
|
|
374
|
-
const
|
375
|
-
const subscription = this.getActiveSubscription(
|
376
|
-
pubsubTopic,
|
377
|
-
peerIdStr
|
378
|
-
);
|
476
|
+
const subscription = this.getActiveSubscription(pubsubTopic);
|
379
477
|
|
380
478
|
if (!subscription) {
|
381
479
|
log.error(
|
@@ -401,7 +499,7 @@ class Filter extends BaseProtocol implements IReceiver {
|
|
401
499
|
}
|
402
500
|
|
403
501
|
export function wakuFilter(
|
404
|
-
init:
|
502
|
+
init: ProtocolCreateOptions = { pubsubTopics: [] }
|
405
503
|
): (libp2p: Libp2p) => IFilter {
|
406
504
|
return (libp2p: Libp2p) => new Filter(libp2p, init);
|
407
505
|
}
|
package/src/lib/filterPeers.ts
CHANGED
@@ -1,24 +1,32 @@
|
|
1
|
-
import { Peer } from "@libp2p/interface
|
1
|
+
import { Peer } from "@libp2p/interface";
|
2
2
|
import { Tags } from "@waku/interfaces";
|
3
3
|
|
4
4
|
/**
|
5
|
-
* Retrieves a list of peers based on the specified criteria
|
5
|
+
* Retrieves a list of peers based on the specified criteria:
|
6
|
+
* 1. If numPeers is 0, return all peers
|
7
|
+
* 2. Bootstrap peers are prioritized
|
8
|
+
* 3. Non-bootstrap peers are randomly selected to fill up to numPeers
|
6
9
|
*
|
7
10
|
* @param peers - The list of peers to filter from.
|
8
|
-
* @param numPeers - The total number of peers to retrieve. If 0, all peers are returned
|
11
|
+
* @param numPeers - The total number of peers to retrieve. If 0, all peers are returned, irrespective of `maxBootstrapPeers`.
|
9
12
|
* @param maxBootstrapPeers - The maximum number of bootstrap peers to retrieve.
|
10
|
-
* @returns
|
13
|
+
* @returns An array of peers based on the specified criteria.
|
11
14
|
*/
|
12
|
-
export
|
15
|
+
export function filterPeersByDiscovery(
|
13
16
|
peers: Peer[],
|
14
17
|
numPeers: number,
|
15
18
|
maxBootstrapPeers: number
|
16
|
-
):
|
19
|
+
): Peer[] {
|
17
20
|
// Collect the bootstrap peers up to the specified maximum
|
18
|
-
|
21
|
+
let bootstrapPeers = peers
|
19
22
|
.filter((peer) => peer.tags.has(Tags.BOOTSTRAP))
|
20
23
|
.slice(0, maxBootstrapPeers);
|
21
24
|
|
25
|
+
// If numPeers is less than the number of bootstrap peers, adjust the bootstrapPeers array
|
26
|
+
if (numPeers > 0 && numPeers < bootstrapPeers.length) {
|
27
|
+
bootstrapPeers = bootstrapPeers.slice(0, numPeers);
|
28
|
+
}
|
29
|
+
|
22
30
|
// Collect non-bootstrap peers
|
23
31
|
const nonBootstrapPeers = peers.filter(
|
24
32
|
(peer) => !peer.tags.has(Tags.BOOTSTRAP)
|
@@ -1,10 +1,9 @@
|
|
1
|
-
import type { PeerId } from "@libp2p/interface
|
2
|
-
import type {
|
1
|
+
import type { PeerId, PeerStore } from "@libp2p/interface";
|
2
|
+
import type { PingService } from "@libp2p/ping";
|
3
3
|
import type { IRelay, PeerIdStr } from "@waku/interfaces";
|
4
4
|
import type { KeepAliveOptions } from "@waku/interfaces";
|
5
|
-
import { Logger } from "@waku/utils";
|
5
|
+
import { Logger, pubsubTopicToSingleShardInfo } from "@waku/utils";
|
6
6
|
import { utf8ToBytes } from "@waku/utils/bytes";
|
7
|
-
import type { PingService } from "libp2p/ping";
|
8
7
|
|
9
8
|
import { createEncoder } from "./message/version_0.js";
|
10
9
|
|
@@ -129,7 +128,7 @@ export class KeepAliveManager {
|
|
129
128
|
if (!meshPeers.includes(peerIdStr)) continue;
|
130
129
|
|
131
130
|
const encoder = createEncoder({
|
132
|
-
|
131
|
+
pubsubTopicShardInfo: pubsubTopicToSingleShardInfo(topic),
|
133
132
|
contentTopic: RelayPingContentTopic,
|
134
133
|
ephemeral: true
|
135
134
|
});
|
@@ -1,12 +1,10 @@
|
|
1
|
-
import type { Stream } from "@libp2p/interface
|
2
|
-
import type { PeerId } from "@libp2p/interface/peer-id";
|
1
|
+
import type { PeerId, Stream } from "@libp2p/interface";
|
3
2
|
import {
|
4
3
|
IEncoder,
|
5
4
|
ILightPush,
|
6
5
|
IMessage,
|
7
6
|
Libp2p,
|
8
7
|
ProtocolCreateOptions,
|
9
|
-
PubsubTopic,
|
10
8
|
SendError,
|
11
9
|
SendResult
|
12
10
|
} from "@waku/interfaces";
|
@@ -22,7 +20,6 @@ import { pipe } from "it-pipe";
|
|
22
20
|
import { Uint8ArrayList } from "uint8arraylist";
|
23
21
|
|
24
22
|
import { BaseProtocol } from "../base_protocol.js";
|
25
|
-
import { DefaultPubsubTopic } from "../constants.js";
|
26
23
|
|
27
24
|
import { PushRpc } from "./push_rpc.js";
|
28
25
|
|
@@ -45,12 +42,14 @@ type PreparePushMessageResult =
|
|
45
42
|
* Implements the [Waku v2 Light Push protocol](https://rfc.vac.dev/spec/19/).
|
46
43
|
*/
|
47
44
|
class LightPush extends BaseProtocol implements ILightPush {
|
48
|
-
private readonly pubsubTopics: PubsubTopic[];
|
49
|
-
private readonly NUM_PEERS_PROTOCOL = 1;
|
50
|
-
|
51
45
|
constructor(libp2p: Libp2p, options?: ProtocolCreateOptions) {
|
52
|
-
super(
|
53
|
-
|
46
|
+
super(
|
47
|
+
LightPushCodec,
|
48
|
+
libp2p.components,
|
49
|
+
log,
|
50
|
+
options!.pubsubTopics!,
|
51
|
+
options
|
52
|
+
);
|
54
53
|
}
|
55
54
|
|
56
55
|
private async preparePushMessage(
|
@@ -109,10 +108,9 @@ class LightPush extends BaseProtocol implements ILightPush {
|
|
109
108
|
};
|
110
109
|
}
|
111
110
|
|
112
|
-
//TODO: get a relevant peer for the topic/shard
|
113
111
|
const peers = await this.getPeers({
|
114
112
|
maxBootstrapPeers: 1,
|
115
|
-
numPeers: this.
|
113
|
+
numPeers: this.numPeersToUse
|
116
114
|
});
|
117
115
|
|
118
116
|
if (!peers.length) {
|
@@ -178,6 +176,8 @@ class LightPush extends BaseProtocol implements ILightPush {
|
|
178
176
|
});
|
179
177
|
|
180
178
|
const results = await Promise.allSettled(promises);
|
179
|
+
|
180
|
+
// TODO: handle renewing faulty peers with new peers (https://github.com/waku-org/js-waku/issues/1463)
|
181
181
|
const errors = results
|
182
182
|
.filter(
|
183
183
|
(
|