@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.
Files changed (86) hide show
  1. package/CHANGELOG.md +78 -2
  2. package/bundle/browser-2f1afe46.js +726 -0
  3. package/bundle/index.js +9166 -12074
  4. package/bundle/lib/base_protocol.js +108 -0
  5. package/bundle/lib/message/topic_only_message.js +3 -2
  6. package/bundle/lib/message/version_0.js +3 -2
  7. package/bundle/peer_exchange-1229c8b0.js +4302 -0
  8. package/bundle/{topic_only_message-ece0fef9.js → topic_only_message-e8406994.js} +12 -8
  9. package/bundle/{version_0-b1fc527d.js → version_0-e9a6cfb0.js} +35 -35
  10. package/dist/.tsbuildinfo +1 -0
  11. package/dist/index.d.ts +4 -1
  12. package/dist/index.js +3 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/lib/base_protocol.d.ts +21 -0
  15. package/dist/lib/base_protocol.js +33 -0
  16. package/dist/lib/base_protocol.js.map +1 -0
  17. package/dist/lib/connection_manager.d.ts +31 -0
  18. package/dist/lib/connection_manager.js +146 -0
  19. package/dist/lib/connection_manager.js.map +1 -0
  20. package/dist/lib/filter/filter_rpc.d.ts +8 -8
  21. package/dist/lib/filter/filter_rpc.js +6 -6
  22. package/dist/lib/filter/index.d.ts +5 -22
  23. package/dist/lib/filter/index.js +31 -86
  24. package/dist/lib/filter/index.js.map +1 -1
  25. package/dist/lib/group_by.d.ts +1 -1
  26. package/dist/lib/group_by.js.map +1 -1
  27. package/dist/lib/keep_alive_manager.d.ts +17 -0
  28. package/dist/lib/keep_alive_manager.js +62 -0
  29. package/dist/lib/keep_alive_manager.js.map +1 -0
  30. package/dist/lib/light_push/index.d.ts +3 -19
  31. package/dist/lib/light_push/index.js +13 -39
  32. package/dist/lib/light_push/index.js.map +1 -1
  33. package/dist/lib/light_push/push_rpc.d.ts +5 -5
  34. package/dist/lib/light_push/push_rpc.js +6 -6
  35. package/dist/lib/message/topic_only_message.d.ts +5 -3
  36. package/dist/lib/message/topic_only_message.js +8 -5
  37. package/dist/lib/message/topic_only_message.js.map +1 -1
  38. package/dist/lib/message/version_0.d.ts +12 -12
  39. package/dist/lib/message/version_0.js +29 -30
  40. package/dist/lib/message/version_0.js.map +1 -1
  41. package/dist/lib/predefined_bootstrap_nodes.js +1 -1
  42. package/dist/lib/predefined_bootstrap_nodes.js.map +1 -1
  43. package/dist/lib/relay/index.d.ts +4 -18
  44. package/dist/lib/relay/index.js +47 -26
  45. package/dist/lib/relay/index.js.map +1 -1
  46. package/dist/lib/relay/message_validator.d.ts +4 -0
  47. package/dist/lib/relay/message_validator.js +25 -0
  48. package/dist/lib/relay/message_validator.js.map +1 -0
  49. package/dist/lib/store/history_rpc.d.ts +4 -4
  50. package/dist/lib/store/history_rpc.js +9 -9
  51. package/dist/lib/store/history_rpc.js.map +1 -1
  52. package/dist/lib/store/index.d.ts +4 -25
  53. package/dist/lib/store/index.js +20 -37
  54. package/dist/lib/store/index.js.map +1 -1
  55. package/dist/lib/to_proto_message.js +3 -2
  56. package/dist/lib/to_proto_message.js.map +1 -1
  57. package/dist/lib/wait_for_remote_peer.js +12 -23
  58. package/dist/lib/wait_for_remote_peer.js.map +1 -1
  59. package/dist/lib/waku.d.ts +8 -15
  60. package/dist/lib/waku.js +34 -97
  61. package/dist/lib/waku.js.map +1 -1
  62. package/package.json +50 -61
  63. package/src/index.ts +11 -3
  64. package/src/lib/base_protocol.ts +47 -0
  65. package/src/lib/connection_manager.ts +220 -0
  66. package/src/lib/filter/filter_rpc.ts +10 -10
  67. package/src/lib/filter/index.ts +52 -147
  68. package/src/lib/group_by.ts +1 -1
  69. package/src/lib/keep_alive_manager.ts +89 -0
  70. package/src/lib/light_push/index.ts +17 -78
  71. package/src/lib/light_push/push_rpc.ts +9 -9
  72. package/src/lib/message/topic_only_message.ts +11 -5
  73. package/src/lib/message/version_0.ts +42 -37
  74. package/src/lib/predefined_bootstrap_nodes.ts +1 -1
  75. package/src/lib/relay/index.ts +77 -52
  76. package/src/lib/relay/message_validator.ts +35 -0
  77. package/src/lib/store/history_rpc.ts +12 -12
  78. package/src/lib/store/index.ts +30 -84
  79. package/src/lib/to_proto_message.ts +3 -2
  80. package/src/lib/wait_for_remote_peer.ts +13 -29
  81. package/src/lib/waku.ts +54 -136
  82. package/bundle/peer_exchange-53df2b11.js +0 -11824
  83. package/dist/lib/random_subset.d.ts +0 -4
  84. package/dist/lib/random_subset.js +0 -25
  85. package/dist/lib/random_subset.js.map +0 -1
  86. 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 FilterRPC {
12
- public constructor(public proto: proto.FilterRPC) {}
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
- ): FilterRPC {
20
- return new FilterRPC({
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 FilterRPC
34
+ * @returns FilterRpc
35
35
  */
36
- static decode(bytes: Uint8Array): FilterRPC {
37
- const res = proto.FilterRPC.decode(bytes);
38
- return new FilterRPC(res);
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.FilterRPC.encode(this.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 | undefined {
53
+ get requestId(): string {
54
54
  return this.proto.requestId;
55
55
  }
56
56
  }
@@ -1,35 +1,26 @@
1
- import type { Stream } from "@libp2p/interface-connection";
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
- IMessage,
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, FilterRPC } from "./filter_rpc.js";
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
- pubSubTopic: string;
69
- private subscriptions: Map<string, Callback<any>>;
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 components: FilterComponents, options?: CreateOptions) {
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.decoders = new Map();
78
- this.pubSubTopic = options?.pubSubTopic ?? DefaultPubSubTopic;
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 topic = opts?.pubSubTopic ?? this.pubSubTopic;
71
+ const { pubSubTopic = DefaultPubSubTopic } = this.options;
96
72
 
97
- const groupedDecoders = groupByContentTopic(decoders);
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 = FilterRPC.createRequest(
104
- topic,
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
- this.addDecoders(groupedDecoders);
142
- this.addCallback(requestId, callback);
112
+ const subscription: Subscription<T> = { callback, decoders, pubSubTopic };
113
+ this.subscriptions.set(requestId, subscription);
143
114
 
144
115
  return async () => {
145
- await this.unsubscribe(topic, contentFilters, requestId, peer);
146
- this.deleteDecoders(groupedDecoders);
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 = FilterRPC.decode(bytes.slice());
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 callback = this.subscriptions.get(requestId);
183
- if (!callback) {
184
- log(`No callback registered for request ID ${requestId}`);
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
- const decoders = this.decoders.get(contentTopic);
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 (msg) return;
207
- const decoded = await dec.fromProtoObj(toProtoMessage(protoMessage));
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
- msg = decoded;
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 = FilterRPC.createRequest(
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<CreateOptions> = {}
311
- ): (components: FilterComponents) => IFilter {
312
- return (components: FilterComponents) => new Filter(components, init);
215
+ init: Partial<ProtocolCreateOptions> = {}
216
+ ): (libp2p: Libp2p) => IFilter {
217
+ return (libp2p: Libp2p) => new Filter(libp2p, init);
313
218
  }
@@ -1,5 +1,5 @@
1
1
  export function groupByContentTopic<T extends { contentTopic: string }>(
2
- values: T[]
2
+ values: readonly T[]
3
3
  ): Map<string, Array<T>> {
4
4
  const groupedDecoders = new Map();
5
5
  values.forEach((value) => {
@@ -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 { ConnectionManager } from "@libp2p/interface-connection-manager";
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 { PushRPC } from "./push_rpc.js";
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
- pubSubTopic: string;
31
+ class LightPush extends BaseProtocol implements ILightPush {
32
+ options: ProtocolCreateOptions;
56
33
 
57
- constructor(public components: LightPushComponents, options?: CreateOptions) {
58
- this.pubSubTopic = options?.pubSubTopic ?? DefaultPubSubTopic;
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 = opts?.pubSubTopic ? opts.pubSubTopic : this.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
- if (!connection) throw "Failed to get a connection to the peer";
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 = PushRPC.createRequest(protoMessage, pubSubTopic);
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 = PushRPC.decode(bytes).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<CreateOptions> = {}
154
- ): (components: LightPushComponents) => ILightPush {
155
- return (components: LightPushComponents) => new LightPush(components, init);
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 PushRPC {
6
- public constructor(public proto: proto.PushRPC) {}
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
- ): PushRPC {
12
- return new PushRPC({
11
+ ): PushRpc {
12
+ return new PushRpc({
13
13
  requestId: uuid(),
14
14
  request: {
15
15
  message: message,
16
- pubSubTopic: pubSubTopic,
16
+ pubsubTopic: pubSubTopic,
17
17
  },
18
18
  response: undefined,
19
19
  });
20
20
  }
21
21
 
22
- static decode(bytes: Uint8ArrayList): PushRPC {
23
- const res = proto.PushRPC.decode(bytes);
24
- return new PushRPC(res);
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.PushRPC.encode(this.proto);
28
+ return proto.PushRpc.encode(this.proto);
29
29
  }
30
30
 
31
31
  get query(): proto.PushRequest | undefined {