@waku/core 0.0.27 → 0.0.28-b5e8b17.0

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.
@@ -1,18 +1,16 @@
1
- import type { PeerId, Stream } from "@libp2p/interface";
1
+ import type { Peer, PeerId, Stream } from "@libp2p/interface";
2
2
  import {
3
+ Failure,
4
+ IBaseProtocolCore,
3
5
  IEncoder,
4
- ILightPush,
5
6
  IMessage,
6
7
  Libp2p,
7
8
  ProtocolCreateOptions,
8
- SendError,
9
- SendResult
9
+ ProtocolError,
10
+ ProtocolResult
10
11
  } from "@waku/interfaces";
11
12
  import { PushResponse } from "@waku/proto";
12
- import {
13
- ensurePubsubTopicIsConfigured,
14
- isMessageSizeUnderCap
15
- } from "@waku/utils";
13
+ import { isMessageSizeUnderCap } from "@waku/utils";
16
14
  import { Logger } from "@waku/utils";
17
15
  import all from "it-all";
18
16
  import * as lp from "it-length-prefixed";
@@ -28,20 +26,14 @@ const log = new Logger("light-push");
28
26
  export const LightPushCodec = "/vac/waku/lightpush/2.0.0-beta1";
29
27
  export { PushResponse };
30
28
 
31
- type PreparePushMessageResult =
32
- | {
33
- query: PushRpc;
34
- error: null;
35
- }
36
- | {
37
- query: null;
38
- error: SendError;
39
- };
29
+ type PreparePushMessageResult = ProtocolResult<"query", PushRpc>;
30
+
31
+ type CoreSendResult = ProtocolResult<"success", PeerId, "failure", Failure>;
40
32
 
41
33
  /**
42
34
  * Implements the [Waku v2 Light Push protocol](https://rfc.vac.dev/spec/19/).
43
35
  */
44
- class LightPush extends BaseProtocol implements ILightPush {
36
+ export class LightPushCore extends BaseProtocol implements IBaseProtocolCore {
45
37
  constructor(libp2p: Libp2p, options?: ProtocolCreateOptions) {
46
38
  super(
47
39
  LightPushCodec,
@@ -54,18 +46,17 @@ class LightPush extends BaseProtocol implements ILightPush {
54
46
 
55
47
  private async preparePushMessage(
56
48
  encoder: IEncoder,
57
- message: IMessage,
58
- pubsubTopic: string
49
+ message: IMessage
59
50
  ): Promise<PreparePushMessageResult> {
60
51
  try {
61
52
  if (!message.payload || message.payload.length === 0) {
62
53
  log.error("Failed to send waku light push: payload is empty");
63
- return { query: null, error: SendError.EMPTY_PAYLOAD };
54
+ return { query: null, error: ProtocolError.EMPTY_PAYLOAD };
64
55
  }
65
56
 
66
57
  if (!(await isMessageSizeUnderCap(encoder, message))) {
67
58
  log.error("Failed to send waku light push: message is bigger than 1MB");
68
- return { query: null, error: SendError.SIZE_TOO_BIG };
59
+ return { query: null, error: ProtocolError.SIZE_TOO_BIG };
69
60
  }
70
61
 
71
62
  const protoMessage = await encoder.toProtoObj(message);
@@ -73,132 +64,120 @@ class LightPush extends BaseProtocol implements ILightPush {
73
64
  log.error("Failed to encode to protoMessage, aborting push");
74
65
  return {
75
66
  query: null,
76
- error: SendError.ENCODE_FAILED
67
+ error: ProtocolError.ENCODE_FAILED
77
68
  };
78
69
  }
79
70
 
80
- const query = PushRpc.createRequest(protoMessage, pubsubTopic);
71
+ const query = PushRpc.createRequest(protoMessage, encoder.pubsubTopic);
81
72
  return { query, error: null };
82
73
  } catch (error) {
83
74
  log.error("Failed to prepare push message", error);
84
75
 
85
76
  return {
86
77
  query: null,
87
- error: SendError.GENERIC_FAIL
78
+ error: ProtocolError.GENERIC_FAIL
88
79
  };
89
80
  }
90
81
  }
91
82
 
92
- async send(encoder: IEncoder, message: IMessage): Promise<SendResult> {
93
- const { pubsubTopic } = encoder;
94
- ensurePubsubTopicIsConfigured(pubsubTopic, this.pubsubTopics);
95
-
96
- const recipients: PeerId[] = [];
97
-
83
+ async send(
84
+ encoder: IEncoder,
85
+ message: IMessage,
86
+ peer: Peer
87
+ ): Promise<CoreSendResult> {
98
88
  const { query, error: preparationError } = await this.preparePushMessage(
99
89
  encoder,
100
- message,
101
- pubsubTopic
90
+ message
102
91
  );
103
92
 
104
93
  if (preparationError || !query) {
105
94
  return {
106
- recipients,
107
- errors: [preparationError]
95
+ success: null,
96
+ failure: {
97
+ error: preparationError,
98
+ peerId: peer.id
99
+ }
108
100
  };
109
101
  }
110
102
 
111
- const peers = await this.getPeers({
112
- maxBootstrapPeers: 1,
113
- numPeers: this.numPeersToUse
114
- });
115
-
116
- if (!peers.length) {
103
+ let stream: Stream | undefined;
104
+ try {
105
+ stream = await this.getStream(peer);
106
+ } catch (err) {
107
+ log.error(
108
+ `Failed to get a stream for remote peer${peer.id.toString()}`,
109
+ err
110
+ );
117
111
  return {
118
- recipients,
119
- errors: [SendError.NO_PEER_AVAILABLE]
112
+ success: null,
113
+ failure: {
114
+ error: ProtocolError.REMOTE_PEER_FAULT,
115
+ peerId: peer.id
116
+ }
120
117
  };
121
118
  }
122
119
 
123
- const promises = peers.map(async (peer) => {
124
- let stream: Stream | undefined;
125
- try {
126
- stream = await this.getStream(peer);
127
- } catch (err) {
128
- log.error(
129
- `Failed to get a stream for remote peer${peer.id.toString()}`,
130
- err
131
- );
132
- return { recipients, error: SendError.REMOTE_PEER_FAULT };
133
- }
134
-
135
- let res: Uint8ArrayList[] | undefined;
136
- try {
137
- res = await pipe(
138
- [query.encode()],
139
- lp.encode,
140
- stream,
141
- lp.decode,
142
- async (source) => await all(source)
143
- );
144
- } catch (err) {
145
- log.error("Failed to send waku light push request", err);
146
- return { recipients, error: SendError.GENERIC_FAIL };
147
- }
148
-
149
- const bytes = new Uint8ArrayList();
150
- res.forEach((chunk) => {
151
- bytes.append(chunk);
152
- });
153
-
154
- let response: PushResponse | undefined;
155
- try {
156
- response = PushRpc.decode(bytes).response;
157
- } catch (err) {
158
- log.error("Failed to decode push reply", err);
159
- return { recipients, error: SendError.DECODE_FAILED };
160
- }
120
+ let res: Uint8ArrayList[] | undefined;
121
+ try {
122
+ res = await pipe(
123
+ [query.encode()],
124
+ lp.encode,
125
+ stream,
126
+ lp.decode,
127
+ async (source) => await all(source)
128
+ );
129
+ } catch (err) {
130
+ log.error("Failed to send waku light push request", err);
131
+ return {
132
+ success: null,
133
+ failure: {
134
+ error: ProtocolError.GENERIC_FAIL,
135
+ peerId: peer.id
136
+ }
137
+ };
138
+ }
161
139
 
162
- if (!response) {
163
- log.error("Remote peer fault: No response in PushRPC");
164
- return { recipients, error: SendError.REMOTE_PEER_FAULT };
165
- }
140
+ const bytes = new Uint8ArrayList();
141
+ res.forEach((chunk) => {
142
+ bytes.append(chunk);
143
+ });
166
144
 
167
- if (!response.isSuccess) {
168
- log.error("Remote peer rejected the message: ", response.info);
169
- return { recipients, error: SendError.REMOTE_PEER_REJECTED };
170
- }
145
+ let response: PushResponse | undefined;
146
+ try {
147
+ response = PushRpc.decode(bytes).response;
148
+ } catch (err) {
149
+ log.error("Failed to decode push reply", err);
150
+ return {
151
+ success: null,
152
+ failure: {
153
+ error: ProtocolError.DECODE_FAILED,
154
+ peerId: peer.id
155
+ }
156
+ };
157
+ }
171
158
 
172
- recipients.some((recipient) => recipient.equals(peer.id)) ||
173
- recipients.push(peer.id);
159
+ if (!response) {
160
+ log.error("Remote peer fault: No response in PushRPC");
161
+ return {
162
+ success: null,
163
+ failure: {
164
+ error: ProtocolError.REMOTE_PEER_FAULT,
165
+ peerId: peer.id
166
+ }
167
+ };
168
+ }
174
169
 
175
- return { recipients };
176
- });
170
+ if (!response.isSuccess) {
171
+ log.error("Remote peer rejected the message: ", response.info);
172
+ return {
173
+ success: null,
174
+ failure: {
175
+ error: ProtocolError.REMOTE_PEER_REJECTED,
176
+ peerId: peer.id
177
+ }
178
+ };
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
- const errors = results
182
- .filter(
183
- (
184
- result
185
- ): result is PromiseFulfilledResult<{
186
- recipients: PeerId[];
187
- error: SendError | undefined;
188
- }> => result.status === "fulfilled"
189
- )
190
- .map((result) => result.value.error)
191
- .filter((error) => error !== undefined) as SendError[];
192
-
193
- return {
194
- recipients,
195
- errors
196
- };
181
+ return { success: peer.id, failure: null };
197
182
  }
198
183
  }
199
-
200
- export function wakuLightPush(
201
- init: Partial<ProtocolCreateOptions> = {}
202
- ): (libp2p: Libp2p) => ILightPush {
203
- return (libp2p: Libp2p) => new LightPush(libp2p, init);
204
- }
@@ -1,10 +1,12 @@
1
1
  import type { PeerId } from "@libp2p/interface";
2
2
  import { IncomingStreamData } from "@libp2p/interface";
3
- import type {
4
- IMetadata,
5
- Libp2pComponents,
6
- PeerIdStr,
7
- ShardInfo
3
+ import {
4
+ type IMetadata,
5
+ type Libp2pComponents,
6
+ type PeerIdStr,
7
+ ProtocolError,
8
+ QueryResult,
9
+ type ShardInfo
8
10
  } from "@waku/interfaces";
9
11
  import { proto_metadata } from "@waku/proto";
10
12
  import { encodeRelayShard, Logger, shardInfoToPubsubTopics } from "@waku/utils";
@@ -21,7 +23,7 @@ export const MetadataCodec = "/vac/waku/metadata/1.0.0";
21
23
 
22
24
  class Metadata extends BaseProtocol implements IMetadata {
23
25
  private libp2pComponents: Libp2pComponents;
24
- handshakesConfirmed: Set<PeerIdStr> = new Set();
26
+ handshakesConfirmed: Map<PeerIdStr, ShardInfo> = new Map();
25
27
 
26
28
  constructor(
27
29
  public shardInfo: ShardInfo,
@@ -57,13 +59,13 @@ class Metadata extends BaseProtocol implements IMetadata {
57
59
  async (source) => await all(source)
58
60
  );
59
61
 
60
- const remoteShardInfoResponse =
61
- this.decodeMetadataResponse(encodedResponse);
62
+ const { error, shardInfo } = this.decodeMetadataResponse(encodedResponse);
62
63
 
63
- await this.savePeerShardInfo(
64
- connection.remotePeer,
65
- remoteShardInfoResponse
66
- );
64
+ if (error) {
65
+ return;
66
+ }
67
+
68
+ await this.savePeerShardInfo(connection.remotePeer, shardInfo);
67
69
  } catch (error) {
68
70
  log.error("Error handling metadata request", error);
69
71
  }
@@ -72,12 +74,15 @@ class Metadata extends BaseProtocol implements IMetadata {
72
74
  /**
73
75
  * Make a metadata query to a peer
74
76
  */
75
- async query(peerId: PeerId): Promise<ShardInfo> {
77
+ async query(peerId: PeerId): Promise<QueryResult> {
76
78
  const request = proto_metadata.WakuMetadataRequest.encode(this.shardInfo);
77
79
 
78
80
  const peer = await this.peerStore.get(peerId);
79
81
  if (!peer) {
80
- throw new Error(`Peer ${peerId.toString()} not found`);
82
+ return {
83
+ shardInfo: null,
84
+ error: ProtocolError.NO_PEER_AVAILABLE
85
+ };
81
86
  }
82
87
 
83
88
  const stream = await this.getStream(peer);
@@ -90,22 +95,38 @@ class Metadata extends BaseProtocol implements IMetadata {
90
95
  async (source) => await all(source)
91
96
  );
92
97
 
93
- const decodedResponse = this.decodeMetadataResponse(encodedResponse);
98
+ const { error, shardInfo } = this.decodeMetadataResponse(encodedResponse);
99
+
100
+ if (error) {
101
+ return {
102
+ shardInfo: null,
103
+ error
104
+ };
105
+ }
94
106
 
95
- await this.savePeerShardInfo(peerId, decodedResponse);
107
+ await this.savePeerShardInfo(peerId, shardInfo);
96
108
 
97
- return decodedResponse;
109
+ return {
110
+ shardInfo,
111
+ error: null
112
+ };
98
113
  }
99
114
 
100
- public async confirmOrAttemptHandshake(peerId: PeerId): Promise<void> {
101
- if (this.handshakesConfirmed.has(peerId.toString())) return;
102
-
103
- await this.query(peerId);
115
+ public async confirmOrAttemptHandshake(peerId: PeerId): Promise<QueryResult> {
116
+ const shardInfo = this.handshakesConfirmed.get(peerId.toString());
117
+ if (shardInfo) {
118
+ return {
119
+ shardInfo,
120
+ error: null
121
+ };
122
+ }
104
123
 
105
- return;
124
+ return await this.query(peerId);
106
125
  }
107
126
 
108
- private decodeMetadataResponse(encodedResponse: Uint8ArrayList[]): ShardInfo {
127
+ private decodeMetadataResponse(
128
+ encodedResponse: Uint8ArrayList[]
129
+ ): QueryResult {
109
130
  const bytes = new Uint8ArrayList();
110
131
 
111
132
  encodedResponse.forEach((chunk) => {
@@ -115,9 +136,18 @@ class Metadata extends BaseProtocol implements IMetadata {
115
136
  bytes
116
137
  ) as ShardInfo;
117
138
 
118
- if (!response) log.error("Error decoding metadata response");
139
+ if (!response) {
140
+ log.error("Error decoding metadata response");
141
+ return {
142
+ shardInfo: null,
143
+ error: ProtocolError.DECODE_FAILED
144
+ };
145
+ }
119
146
 
120
- return response;
147
+ return {
148
+ shardInfo: response,
149
+ error: null
150
+ };
121
151
  }
122
152
 
123
153
  private async savePeerShardInfo(
@@ -131,7 +161,7 @@ class Metadata extends BaseProtocol implements IMetadata {
131
161
  }
132
162
  });
133
163
 
134
- this.handshakesConfirmed.add(peerId.toString());
164
+ this.handshakesConfirmed.set(peerId.toString(), shardInfo);
135
165
  }
136
166
  }
137
167
 
@@ -1,10 +1,16 @@
1
1
  import type { IdentifyResult } from "@libp2p/interface";
2
- import type { IBaseProtocol, IMetadata, IRelay, Waku } from "@waku/interfaces";
2
+ import type {
3
+ IBaseProtocolCore,
4
+ IMetadata,
5
+ IRelay,
6
+ Waku
7
+ } from "@waku/interfaces";
3
8
  import { Protocols } from "@waku/interfaces";
4
9
  import { Logger } from "@waku/utils";
5
10
  import { pEvent } from "p-event";
6
11
  const log = new Logger("wait-for-remote-peer");
7
12
 
13
+ //TODO: move this function within the Waku class: https://github.com/waku-org/js-waku/issues/1761
8
14
  /**
9
15
  * Wait for a remote peer to be ready given the passed protocols.
10
16
  * Must be used after attempting to connect to nodes, using
@@ -53,7 +59,10 @@ export async function waitForRemotePeer(
53
59
  if (!waku.lightPush)
54
60
  throw new Error("Cannot wait for LightPush peer: protocol not mounted");
55
61
  promises.push(
56
- waitForConnectedPeer(waku.lightPush, waku.libp2p.services.metadata)
62
+ waitForConnectedPeer(
63
+ waku.lightPush.protocol,
64
+ waku.libp2p.services.metadata
65
+ )
57
66
  );
58
67
  }
59
68
 
@@ -76,12 +85,13 @@ export async function waitForRemotePeer(
76
85
  }
77
86
  }
78
87
 
88
+ //TODO: move this function within protocol SDK class: https://github.com/waku-org/js-waku/issues/1761
79
89
  /**
80
90
  * Wait for a peer with the given protocol to be connected.
81
91
  * If sharding is enabled on the node, it will also wait for the peer to be confirmed by the metadata service.
82
92
  */
83
93
  async function waitForConnectedPeer(
84
- protocol: IBaseProtocol,
94
+ protocol: IBaseProtocolCore,
85
95
  metadataService?: IMetadata
86
96
  ): Promise<void> {
87
97
  const codec = protocol.multicodec;