@peerbit/pubsub 1.1.12 → 2.0.1
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/lib/esm/index.d.ts +17 -28
- package/lib/esm/index.js +261 -229
- package/lib/esm/index.js.map +1 -1
- package/package.json +6 -9
- package/src/index.ts +360 -300
package/src/index.ts
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import type { PeerId as Libp2pPeerId } from "@libp2p/interface/peer-id";
|
|
2
2
|
import { logger as logFn } from "@peerbit/logger";
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
AcknowledgeDelivery,
|
|
5
|
+
AnyWhere,
|
|
6
|
+
DataMessage,
|
|
7
|
+
DeliveryMode,
|
|
8
|
+
Message,
|
|
9
|
+
MessageHeader,
|
|
10
|
+
SeekDelivery,
|
|
11
|
+
SilentDelivery
|
|
12
|
+
} from "@peerbit/stream-interface";
|
|
5
13
|
import {
|
|
6
14
|
DirectStream,
|
|
7
15
|
DirectStreamComponents,
|
|
8
16
|
DirectStreamOptions,
|
|
9
17
|
PeerStreams
|
|
10
18
|
} from "@peerbit/stream";
|
|
11
|
-
|
|
12
|
-
import { CodeError } from "@libp2p/interface/errors";
|
|
19
|
+
import { CodeError } from "@libp2p/interface";
|
|
13
20
|
import {
|
|
14
21
|
PubSubMessage,
|
|
15
22
|
Subscribe,
|
|
@@ -17,21 +24,19 @@ import {
|
|
|
17
24
|
toUint8Array,
|
|
18
25
|
Unsubscribe,
|
|
19
26
|
GetSubscribers,
|
|
20
|
-
Subscription,
|
|
21
27
|
UnsubcriptionEvent,
|
|
22
28
|
SubscriptionEvent,
|
|
23
29
|
PubSub,
|
|
24
30
|
DataEvent,
|
|
25
|
-
SubscriptionData
|
|
31
|
+
SubscriptionData,
|
|
32
|
+
PublishEvent
|
|
26
33
|
} from "@peerbit/pubsub-interface";
|
|
27
34
|
import { getPublicKeyFromPeerId, PublicSignKey } from "@peerbit/crypto";
|
|
28
|
-
import { CustomEvent } from "@libp2p/interface
|
|
29
|
-
import { waitFor } from "@peerbit/time";
|
|
30
|
-
import { Connection } from "@libp2p/interface/connection";
|
|
31
|
-
import { equals, startsWith } from "@peerbit/uint8arrays";
|
|
35
|
+
import { CustomEvent } from "@libp2p/interface";
|
|
32
36
|
import { PubSubEvents } from "@peerbit/pubsub-interface";
|
|
33
37
|
|
|
34
|
-
export const logger = logFn({ module: "
|
|
38
|
+
export const logger = logFn({ module: "lazysub", level: "warn" });
|
|
39
|
+
const logError = (e?: { message: string }) => logger.error(e?.message);
|
|
35
40
|
|
|
36
41
|
export interface PeerStreamsInit {
|
|
37
42
|
id: Libp2pPeerId;
|
|
@@ -50,12 +55,12 @@ export class DirectSub extends DirectStream<PubSubEvents> implements PubSub {
|
|
|
50
55
|
public topics: Map<string, Map<string, SubscriptionData>>; // topic -> peers --> Uint8Array subscription metadata (the latest received)
|
|
51
56
|
public peerToTopic: Map<string, Set<string>>; // peer -> topics
|
|
52
57
|
public topicsToPeers: Map<string, Set<string>>; // topic -> peers
|
|
53
|
-
public subscriptions: Map<string, { counter: number
|
|
58
|
+
public subscriptions: Map<string, { counter: number }>; // topic -> subscription ids
|
|
54
59
|
public lastSubscriptionMessages: Map<string, Map<string, DataMessage>> =
|
|
55
60
|
new Map();
|
|
56
61
|
|
|
57
62
|
constructor(components: DirectSubComponents, props?: DirectStreamOptions) {
|
|
58
|
-
super(components, ["
|
|
63
|
+
super(components, ["/lazysub/0.0.0"], props);
|
|
59
64
|
this.subscriptions = new Map();
|
|
60
65
|
this.topics = new Map();
|
|
61
66
|
this.topicsToPeers = new Map();
|
|
@@ -70,17 +75,6 @@ export class DirectSub extends DirectStream<PubSubEvents> implements PubSub {
|
|
|
70
75
|
return super.stop();
|
|
71
76
|
}
|
|
72
77
|
|
|
73
|
-
public async onPeerReachable(publicKey: PublicSignKey) {
|
|
74
|
-
// Aggregate subscribers for my topics through this new peer because if we don't do this we might end up with a situtation where
|
|
75
|
-
// we act as a relay and relay messages for a topic, but don't forward it to this new peer because we never learned about their subscriptions
|
|
76
|
-
await this.requestSubscribers([...this.topics.keys()], publicKey);
|
|
77
|
-
return super.onPeerReachable(publicKey);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
public async onPeerDisconnected(peerId: Libp2pPeerId, conn?: Connection) {
|
|
81
|
-
return super.onPeerDisconnected(peerId, conn);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
78
|
private initializeTopic(topic: string) {
|
|
85
79
|
this.topics.get(topic) || this.topics.set(topic, new Map());
|
|
86
80
|
this.topicsToPeers.get(topic) || this.topicsToPeers.set(topic, new Set());
|
|
@@ -94,56 +88,35 @@ export class DirectSub extends DirectStream<PubSubEvents> implements PubSub {
|
|
|
94
88
|
/**
|
|
95
89
|
* Subscribes to a given topic.
|
|
96
90
|
*/
|
|
97
|
-
|
|
98
|
-
* @param topic,
|
|
99
|
-
* @param data, metadata associated with the subscription, shared with peers
|
|
100
|
-
*/
|
|
101
|
-
async subscribe(topic: string | string[], options?: { data?: Uint8Array }) {
|
|
91
|
+
async subscribe(topic: string) {
|
|
102
92
|
if (!this.started) {
|
|
103
93
|
throw new Error("Pubsub has not started");
|
|
104
94
|
}
|
|
105
95
|
|
|
106
|
-
topic = typeof topic === "string" ? [topic] : topic;
|
|
107
|
-
|
|
108
96
|
const newTopicsForTopicData: string[] = [];
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (difference) {
|
|
118
|
-
prev.data = options?.data;
|
|
119
|
-
newTopicsForTopicData.push(t);
|
|
120
|
-
}
|
|
121
|
-
} else {
|
|
122
|
-
this.subscriptions.set(t, {
|
|
123
|
-
counter: 1,
|
|
124
|
-
data: options?.data
|
|
125
|
-
});
|
|
97
|
+
const prev = this.subscriptions.get(topic);
|
|
98
|
+
if (prev) {
|
|
99
|
+
prev.counter += 1;
|
|
100
|
+
} else {
|
|
101
|
+
this.subscriptions.set(topic, {
|
|
102
|
+
counter: 1
|
|
103
|
+
});
|
|
126
104
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
105
|
+
newTopicsForTopicData.push(topic);
|
|
106
|
+
this.listenForSubscribers(topic);
|
|
130
107
|
}
|
|
131
108
|
|
|
132
109
|
if (newTopicsForTopicData.length > 0) {
|
|
133
110
|
const message = new DataMessage({
|
|
134
111
|
data: toUint8Array(
|
|
135
112
|
new Subscribe({
|
|
136
|
-
|
|
137
|
-
(x) => new Subscription(x, options?.data)
|
|
138
|
-
)
|
|
113
|
+
topics: newTopicsForTopicData
|
|
139
114
|
}).bytes()
|
|
140
|
-
)
|
|
115
|
+
),
|
|
116
|
+
header: new MessageHeader({ mode: new SeekDelivery({ redundancy: 2 }) })
|
|
141
117
|
});
|
|
142
118
|
|
|
143
|
-
await this.publishMessage(
|
|
144
|
-
this.components.peerId,
|
|
145
|
-
await message.sign(this.sign)
|
|
146
|
-
);
|
|
119
|
+
await this.publishMessage(this.publicKey, await message.sign(this.sign));
|
|
147
120
|
}
|
|
148
121
|
}
|
|
149
122
|
|
|
@@ -178,68 +151,51 @@ export class DirectSub extends DirectStream<PubSubEvents> implements PubSub {
|
|
|
178
151
|
}
|
|
179
152
|
}
|
|
180
153
|
if (!subscriptions?.counter || options?.force) {
|
|
181
|
-
this.subscriptions.delete(topic);
|
|
182
|
-
this.topics.delete(topic);
|
|
183
|
-
this.topicsToPeers.delete(topic);
|
|
184
|
-
|
|
185
154
|
await this.publishMessage(
|
|
186
|
-
this.
|
|
155
|
+
this.publicKey,
|
|
187
156
|
await new DataMessage({
|
|
157
|
+
header: new MessageHeader({
|
|
158
|
+
mode: new AnyWhere(/* {
|
|
159
|
+
redundancy: 2,
|
|
160
|
+
to: [...this.getPeersOnTopics([topic])]
|
|
161
|
+
} */)
|
|
162
|
+
}),
|
|
188
163
|
data: toUint8Array(new Unsubscribe({ topics: [topic] }).bytes())
|
|
189
164
|
}).sign(this.sign)
|
|
190
165
|
);
|
|
166
|
+
|
|
167
|
+
this.subscriptions.delete(topic);
|
|
168
|
+
this.topics.delete(topic);
|
|
169
|
+
this.topicsToPeers.delete(topic);
|
|
170
|
+
|
|
191
171
|
return true;
|
|
192
172
|
}
|
|
193
173
|
return false;
|
|
194
174
|
}
|
|
195
175
|
|
|
196
|
-
getSubscribers(topic: string):
|
|
197
|
-
|
|
198
|
-
throw new CodeError("not started yet", "ERR_NOT_STARTED_YET");
|
|
199
|
-
}
|
|
176
|
+
getSubscribers(topic: string): PublicSignKey[] | undefined {
|
|
177
|
+
const remote = this.topics.get(topic.toString());
|
|
200
178
|
|
|
201
|
-
if (
|
|
202
|
-
|
|
179
|
+
if (!remote) {
|
|
180
|
+
return undefined;
|
|
203
181
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
getSubscribersWithData(
|
|
209
|
-
topic: string,
|
|
210
|
-
data: Uint8Array,
|
|
211
|
-
options?: { prefix: boolean }
|
|
212
|
-
): string[] | undefined {
|
|
213
|
-
const map = this.topics.get(topic);
|
|
214
|
-
if (map) {
|
|
215
|
-
const results: string[] = [];
|
|
216
|
-
for (const [peer, info] of map.entries()) {
|
|
217
|
-
if (!info.data) {
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
if (options?.prefix) {
|
|
221
|
-
if (!startsWith(info.data, data)) {
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
} else {
|
|
225
|
-
if (!equals(info.data, data)) {
|
|
226
|
-
continue;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
results.push(peer);
|
|
230
|
-
}
|
|
231
|
-
return results;
|
|
182
|
+
const ret: PublicSignKey[] = [];
|
|
183
|
+
for (const v of remote.values()) {
|
|
184
|
+
ret.push(v.publicKey);
|
|
232
185
|
}
|
|
233
|
-
|
|
186
|
+
if (this.subscriptions.get(topic)) {
|
|
187
|
+
ret.push(this.publicKey);
|
|
188
|
+
}
|
|
189
|
+
return ret;
|
|
234
190
|
}
|
|
235
191
|
|
|
236
|
-
listenForSubscribers(topic: string) {
|
|
192
|
+
private listenForSubscribers(topic: string) {
|
|
237
193
|
this.initializeTopic(topic);
|
|
238
194
|
}
|
|
239
195
|
|
|
240
196
|
async requestSubscribers(
|
|
241
197
|
topic: string | string[],
|
|
242
|
-
|
|
198
|
+
to?: PublicSignKey
|
|
243
199
|
): Promise<void> {
|
|
244
200
|
if (!this.started) {
|
|
245
201
|
throw new CodeError("not started yet", "ERR_NOT_STARTED_YET");
|
|
@@ -259,27 +215,32 @@ export class DirectSub extends DirectStream<PubSubEvents> implements PubSub {
|
|
|
259
215
|
}
|
|
260
216
|
|
|
261
217
|
return this.publishMessage(
|
|
262
|
-
this.
|
|
218
|
+
this.publicKey,
|
|
263
219
|
await new DataMessage({
|
|
264
|
-
|
|
265
|
-
|
|
220
|
+
data: toUint8Array(new GetSubscribers({ topics }).bytes()),
|
|
221
|
+
header: new MessageHeader({
|
|
222
|
+
mode: new SeekDelivery({
|
|
223
|
+
to: to ? [to.hashcode()] : [],
|
|
224
|
+
redundancy: 2
|
|
225
|
+
})
|
|
226
|
+
})
|
|
266
227
|
}).sign(this.sign)
|
|
267
228
|
);
|
|
268
229
|
}
|
|
269
230
|
|
|
270
|
-
|
|
271
|
-
const
|
|
231
|
+
getPeersOnTopics(topics: string[]): Set<string> {
|
|
232
|
+
const newPeers: Set<string> = new Set();
|
|
272
233
|
if (topics?.length) {
|
|
273
234
|
for (const topic of topics) {
|
|
274
235
|
const peersOnTopic = this.topicsToPeers.get(topic.toString());
|
|
275
236
|
if (peersOnTopic) {
|
|
276
237
|
peersOnTopic.forEach((peer) => {
|
|
277
|
-
|
|
238
|
+
newPeers.add(peer);
|
|
278
239
|
});
|
|
279
240
|
}
|
|
280
241
|
}
|
|
281
242
|
}
|
|
282
|
-
return
|
|
243
|
+
return newPeers;
|
|
283
244
|
}
|
|
284
245
|
|
|
285
246
|
/* getStreamsWithTopics(topics: string[], otherPeers?: string[]): PeerStreams[] {
|
|
@@ -290,18 +251,17 @@ export class DirectSub extends DirectStream<PubSubEvents> implements PubSub {
|
|
|
290
251
|
} */
|
|
291
252
|
|
|
292
253
|
async publish(
|
|
293
|
-
data: Uint8Array,
|
|
294
|
-
options
|
|
254
|
+
data: Uint8Array | undefined,
|
|
255
|
+
options?: (
|
|
295
256
|
| {
|
|
296
|
-
topics
|
|
297
|
-
to?: (string | PeerId)[];
|
|
298
|
-
strict?: false;
|
|
257
|
+
topics: string[];
|
|
258
|
+
to?: (string | PublicSignKey | PeerId)[];
|
|
299
259
|
}
|
|
300
260
|
| {
|
|
301
261
|
topics: string[];
|
|
302
|
-
|
|
303
|
-
strict: true;
|
|
262
|
+
mode?: SilentDelivery | SeekDelivery | AcknowledgeDelivery;
|
|
304
263
|
}
|
|
264
|
+
) & { client?: string }
|
|
305
265
|
): Promise<Uint8Array> {
|
|
306
266
|
if (!this.started) {
|
|
307
267
|
throw new Error("Not started");
|
|
@@ -309,36 +269,43 @@ export class DirectSub extends DirectStream<PubSubEvents> implements PubSub {
|
|
|
309
269
|
|
|
310
270
|
const topics =
|
|
311
271
|
(options as { topics: string[] }).topics?.map((x) => x.toString()) || [];
|
|
272
|
+
|
|
312
273
|
const tos =
|
|
313
|
-
options?.to?.map((x) =>
|
|
274
|
+
(options as { to: (string | PublicSignKey | PeerId)[] })?.to?.map((x) =>
|
|
314
275
|
x instanceof PublicSignKey
|
|
315
276
|
? x.hashcode()
|
|
316
277
|
: typeof x === "string"
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
) || this.
|
|
278
|
+
? x
|
|
279
|
+
: getPublicKeyFromPeerId(x).hashcode()
|
|
280
|
+
) || this.getPeersOnTopics(topics);
|
|
320
281
|
|
|
321
282
|
// Embedd topic info before the data so that peers/relays can also use topic info to route messages efficiently
|
|
322
|
-
const dataMessage =
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
283
|
+
const dataMessage = data
|
|
284
|
+
? new PubSubData({
|
|
285
|
+
topics: topics.map((x) => x.toString()),
|
|
286
|
+
data,
|
|
287
|
+
strict: !!(options as { to: string[] })?.to
|
|
288
|
+
})
|
|
289
|
+
: undefined;
|
|
329
290
|
|
|
291
|
+
const bytes = dataMessage?.bytes();
|
|
330
292
|
const message = await this.createMessage(bytes, { ...options, to: tos });
|
|
331
293
|
|
|
332
|
-
if (
|
|
333
|
-
|
|
334
|
-
new CustomEvent("
|
|
335
|
-
detail: new
|
|
294
|
+
if (dataMessage) {
|
|
295
|
+
this.dispatchEvent(
|
|
296
|
+
new CustomEvent("publish", {
|
|
297
|
+
detail: new PublishEvent({
|
|
298
|
+
client: options?.client,
|
|
299
|
+
data: dataMessage,
|
|
300
|
+
message
|
|
301
|
+
})
|
|
336
302
|
})
|
|
337
303
|
);
|
|
338
304
|
}
|
|
339
305
|
|
|
340
306
|
// send to all the other peers
|
|
341
|
-
await this.publishMessage(this.
|
|
307
|
+
await this.publishMessage(this.publicKey, message, undefined);
|
|
308
|
+
|
|
342
309
|
return message.id;
|
|
343
310
|
}
|
|
344
311
|
|
|
@@ -360,18 +327,48 @@ export class DirectSub extends DirectStream<PubSubEvents> implements PubSub {
|
|
|
360
327
|
|
|
361
328
|
return change;
|
|
362
329
|
}
|
|
363
|
-
public onPeerUnreachable(publicKey: PublicSignKey) {
|
|
364
|
-
super.onPeerUnreachable(publicKey);
|
|
365
330
|
|
|
366
|
-
|
|
331
|
+
public async onPeerReachable(publicKey: PublicSignKey) {
|
|
332
|
+
// Aggregate subscribers for my topics through this new peer because if we don't do this we might end up with a situtation where
|
|
333
|
+
// we act as a relay and relay messages for a topic, but don't forward it to this new peer because we never learned about their subscriptions
|
|
334
|
+
/* await this.requestSubscribers([...this.topics.keys()], publicKey); */
|
|
335
|
+
|
|
336
|
+
const resp = super.onPeerReachable(publicKey);
|
|
337
|
+
|
|
338
|
+
const stream = this.peers.get(publicKey.hashcode());
|
|
339
|
+
if (stream && this.subscriptions.size > 0) {
|
|
340
|
+
// is new neighbour
|
|
341
|
+
// tell the peer about all topics we subscribe to
|
|
342
|
+
this.publishMessage(
|
|
343
|
+
this.publicKey,
|
|
344
|
+
await new DataMessage({
|
|
345
|
+
data: toUint8Array(
|
|
346
|
+
new Subscribe({
|
|
347
|
+
topics: [...this.subscriptions.keys()]
|
|
348
|
+
}).bytes()
|
|
349
|
+
),
|
|
350
|
+
header: new MessageHeader({
|
|
351
|
+
mode: new SeekDelivery({ redundancy: 2 })
|
|
352
|
+
})
|
|
353
|
+
}).sign(this.sign)
|
|
354
|
+
),
|
|
355
|
+
[stream];
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return resp;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
public onPeerUnreachable(publicKeyHash: string) {
|
|
362
|
+
super.onPeerUnreachable(publicKeyHash);
|
|
363
|
+
|
|
367
364
|
const peerTopics = this.peerToTopic.get(publicKeyHash);
|
|
368
365
|
|
|
369
|
-
const changed:
|
|
366
|
+
const changed: string[] = [];
|
|
370
367
|
if (peerTopics) {
|
|
371
368
|
for (const topic of peerTopics) {
|
|
372
369
|
const change = this.deletePeerFromTopic(topic, publicKeyHash);
|
|
373
370
|
if (change) {
|
|
374
|
-
changed.push(
|
|
371
|
+
changed.push(topic);
|
|
375
372
|
}
|
|
376
373
|
}
|
|
377
374
|
}
|
|
@@ -380,7 +377,10 @@ export class DirectSub extends DirectStream<PubSubEvents> implements PubSub {
|
|
|
380
377
|
if (changed.length > 0) {
|
|
381
378
|
this.dispatchEvent(
|
|
382
379
|
new CustomEvent<UnsubcriptionEvent>("unsubscribe", {
|
|
383
|
-
detail: new UnsubcriptionEvent(
|
|
380
|
+
detail: new UnsubcriptionEvent(
|
|
381
|
+
this.peerKeyHashToPublicKey.get(publicKeyHash)!,
|
|
382
|
+
changed
|
|
383
|
+
)
|
|
384
384
|
})
|
|
385
385
|
);
|
|
386
386
|
}
|
|
@@ -390,7 +390,7 @@ export class DirectSub extends DirectStream<PubSubEvents> implements PubSub {
|
|
|
390
390
|
message: DataMessage,
|
|
391
391
|
pubsubMessage: Subscribe | Unsubscribe
|
|
392
392
|
) {
|
|
393
|
-
const subscriber = message.
|
|
393
|
+
const subscriber = message.header.signatures!.signatures[0].publicKey!;
|
|
394
394
|
const subscriberKey = subscriber.hashcode(); // Assume first signature is the one who is signing
|
|
395
395
|
|
|
396
396
|
for (const topic of pubsubMessage.topics) {
|
|
@@ -411,223 +411,283 @@ export class DirectSub extends DirectStream<PubSubEvents> implements PubSub {
|
|
|
411
411
|
return true;
|
|
412
412
|
}
|
|
413
413
|
|
|
414
|
+
private addPeersOnTopic(
|
|
415
|
+
message: DataMessage<AcknowledgeDelivery | SilentDelivery | SeekDelivery>,
|
|
416
|
+
topics: string[]
|
|
417
|
+
) {
|
|
418
|
+
const existingPeers: Set<string> = new Set(message.header.mode.to);
|
|
419
|
+
const allPeersOnTopic = this.getPeersOnTopics(topics);
|
|
420
|
+
|
|
421
|
+
for (const existing of existingPeers) {
|
|
422
|
+
allPeersOnTopic.add(existing);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
allPeersOnTopic.delete(this.publicKeyHash);
|
|
426
|
+
message.header.mode.to = [...allPeersOnTopic];
|
|
427
|
+
}
|
|
428
|
+
|
|
414
429
|
async onDataMessage(
|
|
415
|
-
from:
|
|
430
|
+
from: PublicSignKey,
|
|
416
431
|
stream: PeerStreams,
|
|
417
|
-
message: DataMessage
|
|
432
|
+
message: DataMessage,
|
|
433
|
+
seenBefore: number
|
|
418
434
|
) {
|
|
435
|
+
if (!message.data || message.data.length === 0) {
|
|
436
|
+
return super.onDataMessage(from, stream, message, seenBefore);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (this.shouldIgnore(message, seenBefore)) {
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
|
|
419
443
|
const pubsubMessage = PubSubMessage.from(message.data);
|
|
420
444
|
if (pubsubMessage instanceof PubSubData) {
|
|
445
|
+
if (message.header.mode instanceof AnyWhere) {
|
|
446
|
+
throw new Error("Unexpected mode for PubSubData messages");
|
|
447
|
+
}
|
|
448
|
+
|
|
421
449
|
/**
|
|
422
450
|
* See if we know more subscribers of the message topics. If so, add aditional end receivers of the message
|
|
423
451
|
*/
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
452
|
+
|
|
453
|
+
let isForMe: boolean;
|
|
454
|
+
if (pubsubMessage.strict) {
|
|
455
|
+
isForMe =
|
|
456
|
+
!!pubsubMessage.topics.find((topic) =>
|
|
457
|
+
this.subscriptions.has(topic)
|
|
458
|
+
) && !!message.header.mode.to?.find((x) => this.publicKeyHash === x);
|
|
459
|
+
} else {
|
|
460
|
+
isForMe =
|
|
461
|
+
!!pubsubMessage.topics.find((topic) =>
|
|
462
|
+
this.subscriptions.has(topic)
|
|
463
|
+
) ||
|
|
464
|
+
(pubsubMessage.topics.length === 0 &&
|
|
465
|
+
!!message.header.mode.to?.find((x) => this.publicKeyHash === x));
|
|
466
|
+
}
|
|
467
|
+
if (isForMe) {
|
|
468
|
+
if ((await this.maybeVerifyMessage(message)) === false) {
|
|
469
|
+
logger.warn("Recieved message that did not verify PubSubData");
|
|
470
|
+
return false;
|
|
441
471
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
);
|
|
447
|
-
}
|
|
448
|
-
if (!verified) {
|
|
449
|
-
logger.warn("Recieved message that did not verify PubSubData");
|
|
450
|
-
return false;
|
|
451
|
-
}
|
|
472
|
+
|
|
473
|
+
await this.acknowledgeMessage(stream, message, seenBefore);
|
|
474
|
+
|
|
475
|
+
if (seenBefore === 0) {
|
|
452
476
|
this.dispatchEvent(
|
|
453
477
|
new CustomEvent("data", {
|
|
454
|
-
detail: new DataEvent(
|
|
478
|
+
detail: new DataEvent({
|
|
479
|
+
data: pubsubMessage,
|
|
480
|
+
message
|
|
481
|
+
})
|
|
455
482
|
})
|
|
456
483
|
);
|
|
457
484
|
}
|
|
458
485
|
}
|
|
459
486
|
|
|
487
|
+
if (message.header.mode.to) {
|
|
488
|
+
message.header.mode.to = message.header.mode.to.filter(
|
|
489
|
+
(x) => x !== this.publicKeyHash
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
|
|
460
493
|
// Forward
|
|
461
494
|
if (!pubsubMessage.strict) {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
495
|
+
this.addPeersOnTopic(
|
|
496
|
+
message as DataMessage<
|
|
497
|
+
SeekDelivery | SilentDelivery | AcknowledgeDelivery
|
|
498
|
+
>,
|
|
499
|
+
pubsubMessage.topics
|
|
465
500
|
);
|
|
466
|
-
newTos.delete(this.publicKeyHash);
|
|
467
|
-
message.to = [...newTos];
|
|
468
501
|
}
|
|
469
502
|
|
|
470
503
|
// Only relay if we got additional receivers
|
|
471
504
|
// or we are NOT subscribing ourselves (if we are not subscribing ourselves we are)
|
|
472
505
|
// If we are not subscribing ourselves, then we don't have enough information to "stop" message propagation here
|
|
473
506
|
if (
|
|
474
|
-
message.to
|
|
475
|
-
|
|
507
|
+
message.header.mode.to?.length ||
|
|
508
|
+
0 > 0 ||
|
|
509
|
+
!pubsubMessage.topics.find((topic) => this.topics.has(topic)) ||
|
|
510
|
+
message.header.mode instanceof SeekDelivery
|
|
476
511
|
) {
|
|
477
|
-
await this
|
|
512
|
+
// DONT await this since it might introduce a dead-lock
|
|
513
|
+
this.relayMessage(from, message).catch(logError);
|
|
478
514
|
}
|
|
479
|
-
} else
|
|
515
|
+
} else {
|
|
480
516
|
if (!(await message.verify(true))) {
|
|
481
|
-
logger.warn("Recieved message that did not verify
|
|
517
|
+
logger.warn("Recieved message that did not verify Unsubscribe");
|
|
482
518
|
return false;
|
|
483
519
|
}
|
|
484
520
|
|
|
485
|
-
if (message.
|
|
521
|
+
if (message.header.signatures!.signatures.length === 0) {
|
|
486
522
|
logger.warn("Recieved subscription message with no signers");
|
|
487
523
|
return false;
|
|
488
524
|
}
|
|
489
525
|
|
|
490
|
-
|
|
491
|
-
logger.info("Recieved subscription message with no topics");
|
|
492
|
-
return false;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
if (!this.subscriptionMessageIsLatest(message, pubsubMessage)) {
|
|
496
|
-
logger.trace("Recieved old subscription message");
|
|
497
|
-
return false;
|
|
498
|
-
}
|
|
526
|
+
await this.acknowledgeMessage(stream, message, seenBefore);
|
|
499
527
|
|
|
500
|
-
const
|
|
501
|
-
const
|
|
528
|
+
const sender = message.header.signatures!.signatures[0].publicKey!;
|
|
529
|
+
const senderKey = sender.hashcode(); // Assume first signature is the one who is signing
|
|
502
530
|
|
|
503
|
-
|
|
531
|
+
if (pubsubMessage instanceof Subscribe) {
|
|
532
|
+
if (pubsubMessage.topics.length === 0) {
|
|
533
|
+
logger.info("Recieved subscription message with no topics");
|
|
534
|
+
return false;
|
|
535
|
+
}
|
|
504
536
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
if (peers == null) {
|
|
509
|
-
return;
|
|
537
|
+
if (!this.subscriptionMessageIsLatest(message, pubsubMessage)) {
|
|
538
|
+
logger.trace("Recieved old subscription message");
|
|
539
|
+
return false;
|
|
510
540
|
}
|
|
511
541
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
const
|
|
542
|
+
this.initializePeer(sender);
|
|
543
|
+
|
|
544
|
+
const changed: string[] = [];
|
|
545
|
+
pubsubMessage.topics.forEach((topic) => {
|
|
546
|
+
const peers = this.topics.get(topic);
|
|
547
|
+
if (peers == null) {
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// if no subscription data, or new subscription has data (and is newer) then overwrite it.
|
|
552
|
+
// subscription where data is undefined is not intended to replace existing data
|
|
553
|
+
const existingSubscription = peers.get(senderKey);
|
|
515
554
|
|
|
516
|
-
if (
|
|
517
|
-
!existingSubscription ||
|
|
518
|
-
(existingSubscription.timestamp < message.header.timetamp &&
|
|
519
|
-
subscription.data)
|
|
520
|
-
) {
|
|
521
|
-
peers.set(
|
|
522
|
-
subscriberKey,
|
|
523
|
-
new SubscriptionData({
|
|
524
|
-
timestamp: message.header.timetamp, // TODO update timestamps on all messages?
|
|
525
|
-
data: subscription.data,
|
|
526
|
-
publicKey: subscriber
|
|
527
|
-
})
|
|
528
|
-
);
|
|
529
555
|
if (
|
|
530
|
-
!existingSubscription
|
|
531
|
-
|
|
556
|
+
!existingSubscription ||
|
|
557
|
+
existingSubscription.timestamp < message.header.timetamp
|
|
532
558
|
) {
|
|
533
|
-
|
|
559
|
+
peers.set(
|
|
560
|
+
senderKey,
|
|
561
|
+
new SubscriptionData({
|
|
562
|
+
timestamp: message.header.timetamp, // TODO update timestamps on all messages?
|
|
563
|
+
publicKey: sender
|
|
564
|
+
})
|
|
565
|
+
);
|
|
566
|
+
if (!existingSubscription) {
|
|
567
|
+
changed.push(topic);
|
|
568
|
+
}
|
|
534
569
|
}
|
|
535
|
-
}
|
|
536
570
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
// Forward
|
|
549
|
-
await this.relayMessage(from, message);
|
|
550
|
-
} else if (pubsubMessage instanceof Unsubscribe) {
|
|
551
|
-
if (!(await message.verify(true))) {
|
|
552
|
-
logger.warn("Recieved message that did not verify Unsubscribe");
|
|
553
|
-
return false;
|
|
554
|
-
}
|
|
571
|
+
this.topicsToPeers.get(topic)?.add(senderKey);
|
|
572
|
+
this.peerToTopic.get(senderKey)?.add(topic);
|
|
573
|
+
});
|
|
574
|
+
if (changed.length > 0) {
|
|
575
|
+
this.dispatchEvent(
|
|
576
|
+
new CustomEvent<SubscriptionEvent>("subscribe", {
|
|
577
|
+
detail: new SubscriptionEvent(sender, changed)
|
|
578
|
+
})
|
|
579
|
+
);
|
|
555
580
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
581
|
+
// also send back a message telling the remote whether we are subsbscring
|
|
582
|
+
if (message.header.mode instanceof SeekDelivery) {
|
|
583
|
+
// only if Subscribe message is of 'seek' type we will respond with our subscriptions
|
|
584
|
+
const mySubscriptions = changed
|
|
585
|
+
.map((x) => {
|
|
586
|
+
const subscription = this.subscriptions.get(x);
|
|
587
|
+
return subscription ? x : undefined;
|
|
588
|
+
})
|
|
589
|
+
.filter((x) => !!x) as string[];
|
|
590
|
+
|
|
591
|
+
if (mySubscriptions.length > 0) {
|
|
592
|
+
const response = new DataMessage({
|
|
593
|
+
data: toUint8Array(
|
|
594
|
+
new Subscribe({
|
|
595
|
+
topics: mySubscriptions
|
|
596
|
+
}).bytes()
|
|
597
|
+
),
|
|
598
|
+
// needs to be Ack or Silent else we will run into a infite message loop
|
|
599
|
+
header: new MessageHeader({
|
|
600
|
+
mode: new AcknowledgeDelivery({
|
|
601
|
+
redundancy: 2,
|
|
602
|
+
to: [sender.hashcode()]
|
|
603
|
+
})
|
|
604
|
+
})
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
await this.publishMessage(
|
|
608
|
+
this.publicKey,
|
|
609
|
+
await response.sign(this.sign)
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
560
614
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
}
|
|
615
|
+
// Forward
|
|
616
|
+
// DONT await this since it might introduce a dead-lock
|
|
617
|
+
this.relayMessage(from, message).catch(logError);
|
|
618
|
+
} else if (pubsubMessage instanceof Unsubscribe) {
|
|
619
|
+
if (!this.subscriptionMessageIsLatest(message, pubsubMessage)) {
|
|
620
|
+
logger.trace("Recieved old subscription message");
|
|
621
|
+
return false;
|
|
622
|
+
}
|
|
565
623
|
|
|
566
|
-
|
|
567
|
-
const subscriber = message.signatures.signatures[0].publicKey!;
|
|
568
|
-
const subscriberKey = subscriber.hashcode(); // Assume first signature is the one who is signing
|
|
624
|
+
const changed: string[] = [];
|
|
569
625
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
if (change) {
|
|
576
|
-
changed.push(new Subscription(unsubscription.topic, change.data));
|
|
626
|
+
for (const unsubscription of pubsubMessage.topics) {
|
|
627
|
+
const change = this.deletePeerFromTopic(unsubscription, senderKey);
|
|
628
|
+
if (change) {
|
|
629
|
+
changed.push(unsubscription);
|
|
630
|
+
}
|
|
577
631
|
}
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
if (changed.length > 0) {
|
|
581
|
-
this.dispatchEvent(
|
|
582
|
-
new CustomEvent<UnsubcriptionEvent>("unsubscribe", {
|
|
583
|
-
detail: new UnsubcriptionEvent(subscriber, changed)
|
|
584
|
-
})
|
|
585
|
-
);
|
|
586
|
-
}
|
|
587
632
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
633
|
+
if (changed.length > 0) {
|
|
634
|
+
this.dispatchEvent(
|
|
635
|
+
new CustomEvent<UnsubcriptionEvent>("unsubscribe", {
|
|
636
|
+
detail: new UnsubcriptionEvent(sender, changed)
|
|
637
|
+
})
|
|
638
|
+
);
|
|
639
|
+
}
|
|
595
640
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
641
|
+
// Forward
|
|
642
|
+
if (
|
|
643
|
+
message.header.mode instanceof SeekDelivery ||
|
|
644
|
+
message.header.mode instanceof SilentDelivery ||
|
|
645
|
+
message.header.mode instanceof AcknowledgeDelivery
|
|
646
|
+
) {
|
|
647
|
+
this.addPeersOnTopic(
|
|
648
|
+
message as DataMessage<
|
|
649
|
+
SeekDelivery | SilentDelivery | AcknowledgeDelivery
|
|
650
|
+
>,
|
|
651
|
+
pubsubMessage.topics
|
|
652
|
+
);
|
|
601
653
|
}
|
|
602
|
-
}
|
|
603
654
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
);
|
|
613
|
-
return false;
|
|
655
|
+
// DONT await this since it might introduce a dead-lock
|
|
656
|
+
this.relayMessage(from, message).catch(logError);
|
|
657
|
+
} else if (pubsubMessage instanceof GetSubscribers) {
|
|
658
|
+
const subscriptionsToSend: string[] = [];
|
|
659
|
+
for (const topic of pubsubMessage.topics) {
|
|
660
|
+
const subscription = this.subscriptions.get(topic);
|
|
661
|
+
if (subscription) {
|
|
662
|
+
subscriptionsToSend.push(topic);
|
|
614
663
|
}
|
|
615
664
|
}
|
|
616
|
-
this.publishMessage(
|
|
617
|
-
this.components.peerId,
|
|
618
|
-
await new DataMessage({
|
|
619
|
-
data: toUint8Array(
|
|
620
|
-
new Subscribe({
|
|
621
|
-
subscriptions: subscriptionsToSend
|
|
622
|
-
}).bytes()
|
|
623
|
-
)
|
|
624
|
-
}).sign(this.sign),
|
|
625
|
-
[stream]
|
|
626
|
-
); // send back to same stream
|
|
627
|
-
}
|
|
628
665
|
|
|
629
|
-
|
|
630
|
-
|
|
666
|
+
if (subscriptionsToSend.length > 0) {
|
|
667
|
+
// respond
|
|
668
|
+
this.publishMessage(
|
|
669
|
+
this.publicKey,
|
|
670
|
+
await new DataMessage({
|
|
671
|
+
data: toUint8Array(
|
|
672
|
+
new Subscribe({
|
|
673
|
+
topics: subscriptionsToSend
|
|
674
|
+
}).bytes()
|
|
675
|
+
),
|
|
676
|
+
header: new MessageHeader({
|
|
677
|
+
mode: new SilentDelivery({
|
|
678
|
+
redundancy: 2,
|
|
679
|
+
to: [sender.hashcode()]
|
|
680
|
+
})
|
|
681
|
+
})
|
|
682
|
+
}).sign(this.sign),
|
|
683
|
+
[stream]
|
|
684
|
+
); // send back to same stream
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// Forward
|
|
688
|
+
// DONT await this since it might introduce a dead-lock
|
|
689
|
+
this.relayMessage(from, message).catch(logError);
|
|
690
|
+
}
|
|
631
691
|
}
|
|
632
692
|
return true;
|
|
633
693
|
}
|
|
@@ -661,7 +721,7 @@ export const waitForSubscribers = async (
|
|
|
661
721
|
: getPublicKeyFromPeerId(id).hashcode();
|
|
662
722
|
});
|
|
663
723
|
|
|
664
|
-
await libp2p.services.pubsub.requestSubscribers(topic);
|
|
724
|
+
// await libp2p.services.pubsub.requestSubscribers(topic);
|
|
665
725
|
return new Promise<void>((resolve, reject) => {
|
|
666
726
|
let counter = 0;
|
|
667
727
|
const interval = setInterval(async () => {
|
|
@@ -673,7 +733,7 @@ export const waitForSubscribers = async (
|
|
|
673
733
|
);
|
|
674
734
|
}
|
|
675
735
|
try {
|
|
676
|
-
const peers = await libp2p.services.pubsub.
|
|
736
|
+
const peers = await libp2p.services.pubsub.topics.get(topic);
|
|
677
737
|
const hasAllPeers =
|
|
678
738
|
peerIdsToWait
|
|
679
739
|
.map((e) => peers && peers.has(e))
|