@waku/core 0.0.9 → 0.0.11
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 +78 -2
- package/bundle/browser-2f1afe46.js +726 -0
- package/bundle/index.js +9166 -12074
- package/bundle/lib/base_protocol.js +108 -0
- package/bundle/lib/message/topic_only_message.js +3 -2
- package/bundle/lib/message/version_0.js +3 -2
- package/bundle/peer_exchange-1229c8b0.js +4302 -0
- package/bundle/{topic_only_message-ece0fef9.js → topic_only_message-e8406994.js} +12 -8
- package/bundle/{version_0-b1fc527d.js → version_0-e9a6cfb0.js} +35 -35
- package/dist/.tsbuildinfo +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/base_protocol.d.ts +21 -0
- package/dist/lib/base_protocol.js +33 -0
- package/dist/lib/base_protocol.js.map +1 -0
- package/dist/lib/connection_manager.d.ts +31 -0
- package/dist/lib/connection_manager.js +146 -0
- package/dist/lib/connection_manager.js.map +1 -0
- package/dist/lib/filter/filter_rpc.d.ts +8 -8
- package/dist/lib/filter/filter_rpc.js +6 -6
- package/dist/lib/filter/index.d.ts +5 -22
- package/dist/lib/filter/index.js +31 -86
- package/dist/lib/filter/index.js.map +1 -1
- package/dist/lib/group_by.d.ts +1 -1
- package/dist/lib/group_by.js.map +1 -1
- package/dist/lib/keep_alive_manager.d.ts +17 -0
- package/dist/lib/keep_alive_manager.js +62 -0
- package/dist/lib/keep_alive_manager.js.map +1 -0
- package/dist/lib/light_push/index.d.ts +3 -19
- package/dist/lib/light_push/index.js +13 -39
- package/dist/lib/light_push/index.js.map +1 -1
- package/dist/lib/light_push/push_rpc.d.ts +5 -5
- package/dist/lib/light_push/push_rpc.js +6 -6
- package/dist/lib/message/topic_only_message.d.ts +5 -3
- package/dist/lib/message/topic_only_message.js +8 -5
- package/dist/lib/message/topic_only_message.js.map +1 -1
- package/dist/lib/message/version_0.d.ts +12 -12
- package/dist/lib/message/version_0.js +29 -30
- package/dist/lib/message/version_0.js.map +1 -1
- package/dist/lib/predefined_bootstrap_nodes.js +1 -1
- package/dist/lib/predefined_bootstrap_nodes.js.map +1 -1
- package/dist/lib/relay/index.d.ts +4 -18
- package/dist/lib/relay/index.js +47 -26
- package/dist/lib/relay/index.js.map +1 -1
- package/dist/lib/relay/message_validator.d.ts +4 -0
- package/dist/lib/relay/message_validator.js +25 -0
- package/dist/lib/relay/message_validator.js.map +1 -0
- package/dist/lib/store/history_rpc.d.ts +4 -4
- package/dist/lib/store/history_rpc.js +9 -9
- package/dist/lib/store/history_rpc.js.map +1 -1
- package/dist/lib/store/index.d.ts +4 -25
- package/dist/lib/store/index.js +20 -37
- package/dist/lib/store/index.js.map +1 -1
- package/dist/lib/to_proto_message.js +3 -2
- package/dist/lib/to_proto_message.js.map +1 -1
- package/dist/lib/wait_for_remote_peer.js +12 -23
- package/dist/lib/wait_for_remote_peer.js.map +1 -1
- package/dist/lib/waku.d.ts +8 -15
- package/dist/lib/waku.js +34 -97
- package/dist/lib/waku.js.map +1 -1
- package/package.json +50 -61
- package/src/index.ts +11 -3
- package/src/lib/base_protocol.ts +47 -0
- package/src/lib/connection_manager.ts +220 -0
- package/src/lib/filter/filter_rpc.ts +10 -10
- package/src/lib/filter/index.ts +52 -147
- package/src/lib/group_by.ts +1 -1
- package/src/lib/keep_alive_manager.ts +89 -0
- package/src/lib/light_push/index.ts +17 -78
- package/src/lib/light_push/push_rpc.ts +9 -9
- package/src/lib/message/topic_only_message.ts +11 -5
- package/src/lib/message/version_0.ts +42 -37
- package/src/lib/predefined_bootstrap_nodes.ts +1 -1
- package/src/lib/relay/index.ts +77 -52
- package/src/lib/relay/message_validator.ts +35 -0
- package/src/lib/store/history_rpc.ts +12 -12
- package/src/lib/store/index.ts +30 -84
- package/src/lib/to_proto_message.ts +3 -2
- package/src/lib/wait_for_remote_peer.ts +13 -29
- package/src/lib/waku.ts +54 -136
- package/bundle/peer_exchange-53df2b11.js +0 -11824
- package/dist/lib/random_subset.d.ts +0 -4
- package/dist/lib/random_subset.js +0 -25
- package/dist/lib/random_subset.js.map +0 -1
- package/src/lib/random_subset.ts +0 -30
@@ -8,16 +8,16 @@ export type ContentFilter = {
|
|
8
8
|
/**
|
9
9
|
* FilterRPC represents a message conforming to the Waku Filter protocol
|
10
10
|
*/
|
11
|
-
export class
|
12
|
-
public constructor(public proto: proto.
|
11
|
+
export class FilterRpc {
|
12
|
+
public constructor(public proto: proto.FilterRpc) {}
|
13
13
|
|
14
14
|
static createRequest(
|
15
15
|
topic: string,
|
16
16
|
contentFilters: ContentFilter[],
|
17
17
|
requestId?: string,
|
18
18
|
subscribe = true
|
19
|
-
):
|
20
|
-
return new
|
19
|
+
): FilterRpc {
|
20
|
+
return new FilterRpc({
|
21
21
|
requestId: requestId || uuid(),
|
22
22
|
request: {
|
23
23
|
subscribe,
|
@@ -31,11 +31,11 @@ export class FilterRPC {
|
|
31
31
|
/**
|
32
32
|
*
|
33
33
|
* @param bytes Uint8Array of bytes from a FilterRPC message
|
34
|
-
* @returns
|
34
|
+
* @returns FilterRpc
|
35
35
|
*/
|
36
|
-
static decode(bytes: Uint8Array):
|
37
|
-
const res = proto.
|
38
|
-
return new
|
36
|
+
static decode(bytes: Uint8Array): FilterRpc {
|
37
|
+
const res = proto.FilterRpc.decode(bytes);
|
38
|
+
return new FilterRpc(res);
|
39
39
|
}
|
40
40
|
|
41
41
|
/**
|
@@ -43,14 +43,14 @@ export class FilterRPC {
|
|
43
43
|
* @returns Uint8Array
|
44
44
|
*/
|
45
45
|
encode(): Uint8Array {
|
46
|
-
return proto.
|
46
|
+
return proto.FilterRpc.encode(this.proto);
|
47
47
|
}
|
48
48
|
|
49
49
|
get push(): proto.MessagePush | undefined {
|
50
50
|
return this.proto.push;
|
51
51
|
}
|
52
52
|
|
53
|
-
get requestId(): string
|
53
|
+
get requestId(): string {
|
54
54
|
return this.proto.requestId;
|
55
55
|
}
|
56
56
|
}
|
package/src/lib/filter/index.ts
CHANGED
@@ -1,35 +1,26 @@
|
|
1
|
-
import type {
|
2
|
-
import type { ConnectionManager } from "@libp2p/interface-connection-manager";
|
3
|
-
import type { PeerId } from "@libp2p/interface-peer-id";
|
4
|
-
import type { PeerStore } from "@libp2p/interface-peer-store";
|
1
|
+
import type { Libp2p } from "@libp2p/interface-libp2p";
|
5
2
|
import type { Peer } from "@libp2p/interface-peer-store";
|
6
3
|
import type { IncomingStreamData } from "@libp2p/interface-registrar";
|
7
|
-
import type { Registrar } from "@libp2p/interface-registrar";
|
8
4
|
import type {
|
9
5
|
Callback,
|
10
6
|
IDecodedMessage,
|
11
7
|
IDecoder,
|
12
8
|
IFilter,
|
13
|
-
|
9
|
+
ProtocolCreateOptions,
|
14
10
|
ProtocolOptions,
|
15
11
|
} from "@waku/interfaces";
|
16
|
-
import {
|
17
|
-
getPeersForProtocol,
|
18
|
-
selectConnection,
|
19
|
-
selectPeerForProtocol,
|
20
|
-
selectRandomPeer,
|
21
|
-
} from "@waku/libp2p-utils";
|
22
12
|
import { WakuMessage as WakuMessageProto } from "@waku/proto";
|
23
13
|
import debug from "debug";
|
24
14
|
import all from "it-all";
|
25
15
|
import * as lp from "it-length-prefixed";
|
26
16
|
import { pipe } from "it-pipe";
|
27
17
|
|
18
|
+
import { BaseProtocol } from "../base_protocol.js";
|
28
19
|
import { DefaultPubSubTopic } from "../constants.js";
|
29
20
|
import { groupByContentTopic } from "../group_by.js";
|
30
21
|
import { toProtoMessage } from "../to_proto_message.js";
|
31
22
|
|
32
|
-
import { ContentFilter,
|
23
|
+
import { ContentFilter, FilterRpc } from "./filter_rpc.js";
|
33
24
|
|
34
25
|
export { ContentFilter };
|
35
26
|
|
@@ -37,25 +28,14 @@ export const FilterCodec = "/vac/waku/filter/2.0.0-beta1";
|
|
37
28
|
|
38
29
|
const log = debug("waku:filter");
|
39
30
|
|
40
|
-
export interface FilterComponents {
|
41
|
-
peerStore: PeerStore;
|
42
|
-
registrar: Registrar;
|
43
|
-
connectionManager: ConnectionManager;
|
44
|
-
}
|
45
|
-
|
46
|
-
export interface CreateOptions {
|
47
|
-
/**
|
48
|
-
* The PubSub Topic to use. Defaults to {@link DefaultPubSubTopic}.
|
49
|
-
*
|
50
|
-
* The usage of the default pubsub topic is recommended.
|
51
|
-
* See [Waku v2 Topic Usage Recommendations](https://rfc.vac.dev/spec/23/) for details.
|
52
|
-
*
|
53
|
-
* @default {@link DefaultPubSubTopic}
|
54
|
-
*/
|
55
|
-
pubSubTopic?: string;
|
56
|
-
}
|
57
|
-
|
58
31
|
export type UnsubscribeFunction = () => Promise<void>;
|
32
|
+
export type RequestID = string;
|
33
|
+
|
34
|
+
type Subscription<T extends IDecodedMessage> = {
|
35
|
+
decoders: IDecoder<T>[];
|
36
|
+
callback: Callback<T>;
|
37
|
+
pubSubTopic: string;
|
38
|
+
};
|
59
39
|
|
60
40
|
/**
|
61
41
|
* Implements client side of the [Waku v2 Filter protocol](https://rfc.vac.dev/spec/12/).
|
@@ -64,20 +44,16 @@ export type UnsubscribeFunction = () => Promise<void>;
|
|
64
44
|
* - https://github.com/status-im/go-waku/issues/245
|
65
45
|
* - https://github.com/status-im/nwaku/issues/948
|
66
46
|
*/
|
67
|
-
class Filter implements IFilter {
|
68
|
-
|
69
|
-
private subscriptions: Map<
|
70
|
-
private decoders: Map<
|
71
|
-
string, // content topic
|
72
|
-
Set<IDecoder<any>>
|
73
|
-
>;
|
47
|
+
class Filter extends BaseProtocol implements IFilter {
|
48
|
+
options: ProtocolCreateOptions;
|
49
|
+
private subscriptions: Map<RequestID, unknown>;
|
74
50
|
|
75
|
-
constructor(public
|
51
|
+
constructor(public libp2p: Libp2p, options?: ProtocolCreateOptions) {
|
52
|
+
super(FilterCodec, libp2p.peerStore, libp2p.getConnections.bind(libp2p));
|
53
|
+
this.options = options ?? {};
|
76
54
|
this.subscriptions = new Map();
|
77
|
-
this.
|
78
|
-
|
79
|
-
this.components.registrar
|
80
|
-
.handle(FilterCodec, this.onRequest.bind(this))
|
55
|
+
this.libp2p
|
56
|
+
.handle(this.multicodec, this.onRequest.bind(this))
|
81
57
|
.catch((e) => log("Failed to register filter protocol", e));
|
82
58
|
}
|
83
59
|
|
@@ -92,26 +68,21 @@ class Filter implements IFilter {
|
|
92
68
|
callback: Callback<T>,
|
93
69
|
opts?: ProtocolOptions
|
94
70
|
): Promise<UnsubscribeFunction> {
|
95
|
-
const
|
71
|
+
const { pubSubTopic = DefaultPubSubTopic } = this.options;
|
96
72
|
|
97
|
-
const
|
98
|
-
const contentTopics = Array.from(groupedDecoders.keys());
|
73
|
+
const contentTopics = Array.from(groupByContentTopic(decoders).keys());
|
99
74
|
|
100
75
|
const contentFilters = contentTopics.map((contentTopic) => ({
|
101
76
|
contentTopic,
|
102
77
|
}));
|
103
|
-
const request =
|
104
|
-
|
78
|
+
const request = FilterRpc.createRequest(
|
79
|
+
pubSubTopic,
|
105
80
|
contentFilters,
|
106
81
|
undefined,
|
107
82
|
true
|
108
83
|
);
|
109
84
|
|
110
85
|
const requestId = request.requestId;
|
111
|
-
if (!requestId)
|
112
|
-
throw new Error(
|
113
|
-
"Internal error: createRequest expected to set `requestId`"
|
114
|
-
);
|
115
86
|
|
116
87
|
const peer = await this.getPeer(opts?.peerId);
|
117
88
|
const stream = await this.newStream(peer);
|
@@ -138,26 +109,21 @@ class Filter implements IFilter {
|
|
138
109
|
throw e;
|
139
110
|
}
|
140
111
|
|
141
|
-
|
142
|
-
this.
|
112
|
+
const subscription: Subscription<T> = { callback, decoders, pubSubTopic };
|
113
|
+
this.subscriptions.set(requestId, subscription);
|
143
114
|
|
144
115
|
return async () => {
|
145
|
-
await this.unsubscribe(
|
146
|
-
this.
|
147
|
-
this.deleteCallback(requestId);
|
116
|
+
await this.unsubscribe(pubSubTopic, contentFilters, requestId, peer);
|
117
|
+
this.subscriptions.delete(requestId);
|
148
118
|
};
|
149
119
|
}
|
150
120
|
|
151
|
-
get peerStore(): PeerStore {
|
152
|
-
return this.components.peerStore;
|
153
|
-
}
|
154
|
-
|
155
121
|
private onRequest(streamData: IncomingStreamData): void {
|
156
122
|
log("Receiving message push");
|
157
123
|
try {
|
158
124
|
pipe(streamData.stream, lp.decode(), async (source) => {
|
159
125
|
for await (const bytes of source) {
|
160
|
-
const res =
|
126
|
+
const res = FilterRpc.decode(bytes.slice());
|
161
127
|
if (res.requestId && res.push?.messages?.length) {
|
162
128
|
await this.pushMessages(res.requestId, res.push.messages);
|
163
129
|
}
|
@@ -175,13 +141,21 @@ class Filter implements IFilter {
|
|
175
141
|
}
|
176
142
|
}
|
177
143
|
|
178
|
-
private async pushMessages(
|
144
|
+
private async pushMessages<T extends IDecodedMessage>(
|
179
145
|
requestId: string,
|
180
146
|
messages: WakuMessageProto[]
|
181
147
|
): Promise<void> {
|
182
|
-
const
|
183
|
-
|
184
|
-
|
148
|
+
const subscription = this.subscriptions.get(requestId) as
|
149
|
+
| Subscription<T>
|
150
|
+
| undefined;
|
151
|
+
if (!subscription) {
|
152
|
+
log(`No subscription locally registered for request ID ${requestId}`);
|
153
|
+
return;
|
154
|
+
}
|
155
|
+
const { decoders, callback, pubSubTopic } = subscription;
|
156
|
+
|
157
|
+
if (!decoders || !decoders.length) {
|
158
|
+
log(`No decoder registered for request ID ${requestId}`);
|
185
159
|
return;
|
186
160
|
}
|
187
161
|
|
@@ -192,72 +166,35 @@ class Filter implements IFilter {
|
|
192
166
|
return;
|
193
167
|
}
|
194
168
|
|
195
|
-
|
196
|
-
if (!decoders) {
|
197
|
-
log("No decoder for", contentTopic);
|
198
|
-
return;
|
199
|
-
}
|
200
|
-
|
201
|
-
let msg: IMessage | undefined;
|
169
|
+
let didDecodeMsg = false;
|
202
170
|
// We don't want to wait for decoding failure, just attempt to decode
|
203
171
|
// all messages and do the call back on the one that works
|
204
172
|
// noinspection ES6MissingAwait
|
205
|
-
decoders.forEach(async (dec) => {
|
206
|
-
if (
|
207
|
-
const decoded = await dec.fromProtoObj(
|
173
|
+
decoders.forEach(async (dec: IDecoder<T>) => {
|
174
|
+
if (didDecodeMsg) return;
|
175
|
+
const decoded = await dec.fromProtoObj(
|
176
|
+
pubSubTopic,
|
177
|
+
toProtoMessage(protoMessage)
|
178
|
+
);
|
208
179
|
if (!decoded) {
|
209
180
|
log("Not able to decode message");
|
210
181
|
return;
|
211
182
|
}
|
212
183
|
// This is just to prevent more decoding attempt
|
213
184
|
// TODO: Could be better if we were to abort promises
|
214
|
-
|
185
|
+
didDecodeMsg = Boolean(decoded);
|
215
186
|
await callback(decoded);
|
216
187
|
});
|
217
188
|
}
|
218
189
|
}
|
219
190
|
|
220
|
-
private addCallback(requestId: string, callback: Callback<any>): void {
|
221
|
-
this.subscriptions.set(requestId, callback);
|
222
|
-
}
|
223
|
-
|
224
|
-
private deleteCallback(requestId: string): void {
|
225
|
-
this.subscriptions.delete(requestId);
|
226
|
-
}
|
227
|
-
|
228
|
-
private addDecoders<T extends IDecodedMessage>(
|
229
|
-
decoders: Map<string, Array<IDecoder<T>>>
|
230
|
-
): void {
|
231
|
-
decoders.forEach((decoders, contentTopic) => {
|
232
|
-
const currDecs = this.decoders.get(contentTopic);
|
233
|
-
if (!currDecs) {
|
234
|
-
this.decoders.set(contentTopic, new Set(decoders));
|
235
|
-
} else {
|
236
|
-
this.decoders.set(contentTopic, new Set([...currDecs, ...decoders]));
|
237
|
-
}
|
238
|
-
});
|
239
|
-
}
|
240
|
-
|
241
|
-
private deleteDecoders<T extends IDecodedMessage>(
|
242
|
-
decoders: Map<string, Array<IDecoder<T>>>
|
243
|
-
): void {
|
244
|
-
decoders.forEach((decoders, contentTopic) => {
|
245
|
-
const currDecs = this.decoders.get(contentTopic);
|
246
|
-
if (currDecs) {
|
247
|
-
decoders.forEach((dec) => {
|
248
|
-
currDecs.delete(dec);
|
249
|
-
});
|
250
|
-
}
|
251
|
-
});
|
252
|
-
}
|
253
|
-
|
254
191
|
private async unsubscribe(
|
255
192
|
topic: string,
|
256
193
|
contentFilters: ContentFilter[],
|
257
194
|
requestId: string,
|
258
195
|
peer: Peer
|
259
196
|
): Promise<void> {
|
260
|
-
const unsubscribeRequest =
|
197
|
+
const unsubscribeRequest = FilterRpc.createRequest(
|
261
198
|
topic,
|
262
199
|
contentFilters,
|
263
200
|
requestId,
|
@@ -272,42 +209,10 @@ class Filter implements IFilter {
|
|
272
209
|
throw e;
|
273
210
|
}
|
274
211
|
}
|
275
|
-
|
276
|
-
private async newStream(peer: Peer): Promise<Stream> {
|
277
|
-
const connections = this.components.connectionManager.getConnections(
|
278
|
-
peer.id
|
279
|
-
);
|
280
|
-
const connection = selectConnection(connections);
|
281
|
-
if (!connection) {
|
282
|
-
throw new Error("Failed to get a connection to the peer");
|
283
|
-
}
|
284
|
-
|
285
|
-
return connection.newStream(FilterCodec);
|
286
|
-
}
|
287
|
-
|
288
|
-
private async getPeer(peerId?: PeerId): Promise<Peer> {
|
289
|
-
const res = await selectPeerForProtocol(
|
290
|
-
this.components.peerStore,
|
291
|
-
[FilterCodec],
|
292
|
-
peerId
|
293
|
-
);
|
294
|
-
if (!res) {
|
295
|
-
throw new Error(`Failed to select peer for ${FilterCodec}`);
|
296
|
-
}
|
297
|
-
return res.peer;
|
298
|
-
}
|
299
|
-
|
300
|
-
async peers(): Promise<Peer[]> {
|
301
|
-
return getPeersForProtocol(this.components.peerStore, [FilterCodec]);
|
302
|
-
}
|
303
|
-
|
304
|
-
async randomPeer(): Promise<Peer | undefined> {
|
305
|
-
return selectRandomPeer(await this.peers());
|
306
|
-
}
|
307
212
|
}
|
308
213
|
|
309
214
|
export function wakuFilter(
|
310
|
-
init: Partial<
|
311
|
-
): (
|
312
|
-
return (
|
215
|
+
init: Partial<ProtocolCreateOptions> = {}
|
216
|
+
): (libp2p: Libp2p) => IFilter {
|
217
|
+
return (libp2p: Libp2p) => new Filter(libp2p, init);
|
313
218
|
}
|
package/src/lib/group_by.ts
CHANGED
@@ -0,0 +1,89 @@
|
|
1
|
+
import type { PeerId } from "@libp2p/interface-peer-id";
|
2
|
+
import type { IRelay } from "@waku/interfaces";
|
3
|
+
import debug from "debug";
|
4
|
+
import type { Libp2p } from "libp2p";
|
5
|
+
|
6
|
+
import { createEncoder } from "../index.js";
|
7
|
+
|
8
|
+
import { RelayPingContentTopic } from "./relay/constants.js";
|
9
|
+
|
10
|
+
const log = debug("waku:keep-alive");
|
11
|
+
|
12
|
+
export interface KeepAliveOptions {
|
13
|
+
pingKeepAlive: number;
|
14
|
+
relayKeepAlive: number;
|
15
|
+
}
|
16
|
+
|
17
|
+
export class KeepAliveManager {
|
18
|
+
private pingKeepAliveTimers: Map<string, ReturnType<typeof setInterval>>;
|
19
|
+
private relayKeepAliveTimers: Map<PeerId, ReturnType<typeof setInterval>>;
|
20
|
+
private options: KeepAliveOptions;
|
21
|
+
private relay?: IRelay;
|
22
|
+
|
23
|
+
constructor(options: KeepAliveOptions, relay?: IRelay) {
|
24
|
+
this.pingKeepAliveTimers = new Map();
|
25
|
+
this.relayKeepAliveTimers = new Map();
|
26
|
+
this.options = options;
|
27
|
+
this.relay = relay;
|
28
|
+
}
|
29
|
+
|
30
|
+
public start(peerId: PeerId, libp2pPing: Libp2p["ping"]): void {
|
31
|
+
// Just in case a timer already exist for this peer
|
32
|
+
this.stop(peerId);
|
33
|
+
|
34
|
+
const { pingKeepAlive: pingPeriodSecs, relayKeepAlive: relayPeriodSecs } =
|
35
|
+
this.options;
|
36
|
+
|
37
|
+
const peerIdStr = peerId.toString();
|
38
|
+
|
39
|
+
if (pingPeriodSecs !== 0) {
|
40
|
+
const interval = setInterval(() => {
|
41
|
+
libp2pPing(peerId).catch((e) => {
|
42
|
+
log(`Ping failed (${peerIdStr})`, e);
|
43
|
+
});
|
44
|
+
}, pingPeriodSecs * 1000);
|
45
|
+
this.pingKeepAliveTimers.set(peerIdStr, interval);
|
46
|
+
}
|
47
|
+
|
48
|
+
const relay = this.relay;
|
49
|
+
if (relay && relayPeriodSecs !== 0) {
|
50
|
+
const encoder = createEncoder({
|
51
|
+
contentTopic: RelayPingContentTopic,
|
52
|
+
ephemeral: true,
|
53
|
+
});
|
54
|
+
const interval = setInterval(() => {
|
55
|
+
log("Sending Waku Relay ping message");
|
56
|
+
relay
|
57
|
+
.send(encoder, { payload: new Uint8Array([1]) })
|
58
|
+
.catch((e) => log("Failed to send relay ping", e));
|
59
|
+
}, relayPeriodSecs * 1000);
|
60
|
+
this.relayKeepAliveTimers.set(peerId, interval);
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
public stop(peerId: PeerId): void {
|
65
|
+
const peerIdStr = peerId.toString();
|
66
|
+
|
67
|
+
if (this.pingKeepAliveTimers.has(peerIdStr)) {
|
68
|
+
clearInterval(this.pingKeepAliveTimers.get(peerIdStr));
|
69
|
+
this.pingKeepAliveTimers.delete(peerIdStr);
|
70
|
+
}
|
71
|
+
|
72
|
+
if (this.relayKeepAliveTimers.has(peerId)) {
|
73
|
+
clearInterval(this.relayKeepAliveTimers.get(peerId));
|
74
|
+
this.relayKeepAliveTimers.delete(peerId);
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
public stopAll(): void {
|
79
|
+
for (const timer of [
|
80
|
+
...Object.values(this.pingKeepAliveTimers),
|
81
|
+
...Object.values(this.relayKeepAliveTimers),
|
82
|
+
]) {
|
83
|
+
clearInterval(timer);
|
84
|
+
}
|
85
|
+
|
86
|
+
this.pingKeepAliveTimers.clear();
|
87
|
+
this.relayKeepAliveTimers.clear();
|
88
|
+
}
|
89
|
+
}
|
@@ -1,20 +1,13 @@
|
|
1
|
-
import {
|
1
|
+
import type { Libp2p } from "@libp2p/interface-libp2p";
|
2
2
|
import type { PeerId } from "@libp2p/interface-peer-id";
|
3
|
-
import type { Peer } from "@libp2p/interface-peer-store";
|
4
|
-
import type { PeerStore } from "@libp2p/interface-peer-store";
|
5
3
|
import type {
|
6
4
|
IEncoder,
|
7
5
|
ILightPush,
|
8
6
|
IMessage,
|
7
|
+
ProtocolCreateOptions,
|
9
8
|
ProtocolOptions,
|
10
9
|
SendResult,
|
11
10
|
} from "@waku/interfaces";
|
12
|
-
import {
|
13
|
-
getPeersForProtocol,
|
14
|
-
selectConnection,
|
15
|
-
selectPeerForProtocol,
|
16
|
-
selectRandomPeer,
|
17
|
-
} from "@waku/libp2p-utils";
|
18
11
|
import { PushResponse } from "@waku/proto";
|
19
12
|
import debug from "debug";
|
20
13
|
import all from "it-all";
|
@@ -22,40 +15,25 @@ import * as lp from "it-length-prefixed";
|
|
22
15
|
import { pipe } from "it-pipe";
|
23
16
|
import { Uint8ArrayList } from "uint8arraylist";
|
24
17
|
|
18
|
+
import { BaseProtocol } from "../base_protocol.js";
|
25
19
|
import { DefaultPubSubTopic } from "../constants.js";
|
26
20
|
|
27
|
-
import {
|
21
|
+
import { PushRpc } from "./push_rpc.js";
|
28
22
|
|
29
23
|
const log = debug("waku:light-push");
|
30
24
|
|
31
25
|
export const LightPushCodec = "/vac/waku/lightpush/2.0.0-beta1";
|
32
26
|
export { PushResponse };
|
33
27
|
|
34
|
-
export interface LightPushComponents {
|
35
|
-
peerStore: PeerStore;
|
36
|
-
connectionManager: ConnectionManager;
|
37
|
-
}
|
38
|
-
|
39
|
-
export interface CreateOptions {
|
40
|
-
/**
|
41
|
-
* The PubSub Topic to use. Defaults to {@link DefaultPubSubTopic}.
|
42
|
-
*
|
43
|
-
* The usage of the default pubsub topic is recommended.
|
44
|
-
* See [Waku v2 Topic Usage Recommendations](https://rfc.vac.dev/spec/23/) for details.
|
45
|
-
*
|
46
|
-
* @default {@link DefaultPubSubTopic}
|
47
|
-
*/
|
48
|
-
pubSubTopic?: string;
|
49
|
-
}
|
50
|
-
|
51
28
|
/**
|
52
29
|
* Implements the [Waku v2 Light Push protocol](https://rfc.vac.dev/spec/19/).
|
53
30
|
*/
|
54
|
-
class LightPush implements ILightPush {
|
55
|
-
|
31
|
+
class LightPush extends BaseProtocol implements ILightPush {
|
32
|
+
options: ProtocolCreateOptions;
|
56
33
|
|
57
|
-
constructor(public
|
58
|
-
|
34
|
+
constructor(public libp2p: Libp2p, options?: ProtocolCreateOptions) {
|
35
|
+
super(LightPushCodec, libp2p.peerStore, libp2p.getConnections.bind(libp2p));
|
36
|
+
this.options = options || {};
|
59
37
|
}
|
60
38
|
|
61
39
|
async push(
|
@@ -63,27 +41,10 @@ class LightPush implements ILightPush {
|
|
63
41
|
message: IMessage,
|
64
42
|
opts?: ProtocolOptions
|
65
43
|
): Promise<SendResult> {
|
66
|
-
const pubSubTopic =
|
67
|
-
|
68
|
-
const res = await selectPeerForProtocol(
|
69
|
-
this.components.peerStore,
|
70
|
-
[LightPushCodec],
|
71
|
-
opts?.peerId
|
72
|
-
);
|
73
|
-
|
74
|
-
if (!res) {
|
75
|
-
throw new Error("Failed to get a peer");
|
76
|
-
}
|
77
|
-
const { peer } = res;
|
78
|
-
|
79
|
-
const connections = this.components.connectionManager.getConnections(
|
80
|
-
peer.id
|
81
|
-
);
|
82
|
-
const connection = selectConnection(connections);
|
44
|
+
const { pubSubTopic = DefaultPubSubTopic } = this.options;
|
83
45
|
|
84
|
-
|
85
|
-
|
86
|
-
const stream = await connection.newStream(LightPushCodec);
|
46
|
+
const peer = await this.getPeer(opts?.peerId);
|
47
|
+
const stream = await this.newStream(peer);
|
87
48
|
|
88
49
|
const recipients: PeerId[] = [];
|
89
50
|
|
@@ -93,7 +54,7 @@ class LightPush implements ILightPush {
|
|
93
54
|
log("Failed to encode to protoMessage, aborting push");
|
94
55
|
return { recipients };
|
95
56
|
}
|
96
|
-
const query =
|
57
|
+
const query = PushRpc.createRequest(protoMessage, pubSubTopic);
|
97
58
|
const res = await pipe(
|
98
59
|
[query.encode()],
|
99
60
|
lp.encode(),
|
@@ -107,7 +68,7 @@ class LightPush implements ILightPush {
|
|
107
68
|
bytes.append(chunk);
|
108
69
|
});
|
109
70
|
|
110
|
-
const response =
|
71
|
+
const response = PushRpc.decode(bytes).response;
|
111
72
|
|
112
73
|
if (!response) {
|
113
74
|
log("No response in PushRPC");
|
@@ -125,32 +86,10 @@ class LightPush implements ILightPush {
|
|
125
86
|
}
|
126
87
|
return { recipients };
|
127
88
|
}
|
128
|
-
|
129
|
-
/**
|
130
|
-
* Returns known peers from the address book (`libp2p.peerStore`) that support
|
131
|
-
* light push protocol. Waku may or may not be currently connected to these
|
132
|
-
* peers.
|
133
|
-
*/
|
134
|
-
async peers(): Promise<Peer[]> {
|
135
|
-
return getPeersForProtocol(this.components.peerStore, [LightPushCodec]);
|
136
|
-
}
|
137
|
-
|
138
|
-
/**
|
139
|
-
* Returns a random peer that supports light push protocol from the address
|
140
|
-
* book (`libp2p.peerStore`). Waku may or may not be currently connected to
|
141
|
-
* this peer.
|
142
|
-
*/
|
143
|
-
async randomPeer(): Promise<Peer | undefined> {
|
144
|
-
return selectRandomPeer(await this.peers());
|
145
|
-
}
|
146
|
-
|
147
|
-
get peerStore(): PeerStore {
|
148
|
-
return this.components.peerStore;
|
149
|
-
}
|
150
89
|
}
|
151
90
|
|
152
91
|
export function wakuLightPush(
|
153
|
-
init: Partial<
|
154
|
-
): (
|
155
|
-
return (
|
92
|
+
init: Partial<ProtocolCreateOptions> = {}
|
93
|
+
): (libp2p: Libp2p) => ILightPush {
|
94
|
+
return (libp2p: Libp2p) => new LightPush(libp2p, init);
|
156
95
|
}
|
@@ -2,30 +2,30 @@ import { proto_lightpush as proto } from "@waku/proto";
|
|
2
2
|
import type { Uint8ArrayList } from "uint8arraylist";
|
3
3
|
import { v4 as uuid } from "uuid";
|
4
4
|
|
5
|
-
export class
|
6
|
-
public constructor(public proto: proto.
|
5
|
+
export class PushRpc {
|
6
|
+
public constructor(public proto: proto.PushRpc) {}
|
7
7
|
|
8
8
|
static createRequest(
|
9
9
|
message: proto.WakuMessage,
|
10
10
|
pubSubTopic: string
|
11
|
-
):
|
12
|
-
return new
|
11
|
+
): PushRpc {
|
12
|
+
return new PushRpc({
|
13
13
|
requestId: uuid(),
|
14
14
|
request: {
|
15
15
|
message: message,
|
16
|
-
|
16
|
+
pubsubTopic: pubSubTopic,
|
17
17
|
},
|
18
18
|
response: undefined,
|
19
19
|
});
|
20
20
|
}
|
21
21
|
|
22
|
-
static decode(bytes: Uint8ArrayList):
|
23
|
-
const res = proto.
|
24
|
-
return new
|
22
|
+
static decode(bytes: Uint8ArrayList): PushRpc {
|
23
|
+
const res = proto.PushRpc.decode(bytes);
|
24
|
+
return new PushRpc(res);
|
25
25
|
}
|
26
26
|
|
27
27
|
encode(): Uint8Array {
|
28
|
-
return proto.
|
28
|
+
return proto.PushRpc.encode(this.proto);
|
29
29
|
}
|
30
30
|
|
31
31
|
get query(): proto.PushRequest | undefined {
|