@peerbit/pubsub 1.1.13 → 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/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
- import { DataMessage } from "@peerbit/stream-interface";
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/events";
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: "direct-sub", level: "warn" });
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; data?: Uint8Array }>; // topic -> subscription ids
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, ["pubsub/0.0.0"], props);
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
- for (const t of topic) {
110
- const prev = this.subscriptions.get(t);
111
- if (prev) {
112
- const difference =
113
- !!prev.data != !!options?.data ||
114
- (prev.data && options?.data && !equals(prev.data, options?.data));
115
- prev.counter += 1;
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
- newTopicsForTopicData.push(t);
128
- this.listenForSubscribers(t);
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
- subscriptions: newTopicsForTopicData.map(
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.components.peerId,
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): Map<string, SubscriptionData> | undefined {
197
- if (!this.started) {
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 (topic == null) {
202
- throw new CodeError("topic is required", "ERR_NOT_VALID_TOPIC");
179
+ if (!remote) {
180
+ return undefined;
203
181
  }
204
-
205
- return this.topics.get(topic.toString());
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
- return;
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
- from?: PublicSignKey
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.components.peerId,
218
+ this.publicKey,
263
219
  await new DataMessage({
264
- to: from ? [from.hashcode()] : [],
265
- data: toUint8Array(new GetSubscribers({ topics }).bytes())
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
- getPeersWithTopics(topics: string[], otherPeers?: string[]): Set<string> {
271
- const peers: Set<string> = otherPeers ? new Set(otherPeers) : new Set();
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
- peers.add(peer);
238
+ newPeers.add(peer);
278
239
  });
279
240
  }
280
241
  }
281
242
  }
282
- return peers;
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?: string[];
297
- to?: (string | PeerId)[];
298
- strict?: false;
257
+ topics: string[];
258
+ to?: (string | PublicSignKey | PeerId)[];
299
259
  }
300
260
  | {
301
261
  topics: string[];
302
- to: (string | PeerId)[];
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
- ? x
318
- : getPublicKeyFromPeerId(x).hashcode()
319
- ) || this.getPeersWithTopics(topics);
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 = new PubSubData({
323
- topics: topics.map((x) => x.toString()),
324
- data,
325
- strict: options.strict
326
- });
327
-
328
- const bytes = dataMessage.bytes();
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 (this.emitSelf) {
333
- super.dispatchEvent(
334
- new CustomEvent("data", {
335
- detail: new DataEvent(dataMessage, message)
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.components.peerId, message, undefined);
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
- const publicKeyHash = publicKey.hashcode();
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: Subscription[] = [];
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(new Subscription(topic, change.data));
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(publicKey, changed)
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.signatures.signatures[0].publicKey!;
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: Libp2pPeerId,
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
- let verified: boolean | undefined = undefined;
425
-
426
- const isFromSelf = this.components.peerId.equals(from);
427
- if (!isFromSelf || this.emitSelf) {
428
- let isForMe: boolean;
429
- if (pubsubMessage.strict) {
430
- isForMe =
431
- !!pubsubMessage.topics.find((topic) =>
432
- this.subscriptions.has(topic)
433
- ) && !!message.to.find((x) => this.publicKeyHash === x);
434
- } else {
435
- isForMe =
436
- !!pubsubMessage.topics.find((topic) =>
437
- this.subscriptions.has(topic)
438
- ) ||
439
- (pubsubMessage.topics.length === 0 &&
440
- !!message.to.find((x) => this.publicKeyHash === x));
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
- if (isForMe) {
443
- if (verified === undefined) {
444
- verified = await message.verify(
445
- this.signaturePolicy === "StictSign" ? true : false
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(pubsubMessage, message)
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
- const newTos = this.getPeersWithTopics(
463
- pubsubMessage.topics,
464
- message.to
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.length > 0 ||
475
- !pubsubMessage.topics.find((topic) => this.topics.has(topic))
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.relayMessage(from, message);
512
+ // DONT await this since it might introduce a dead-lock
513
+ this.relayMessage(from, message).catch(logError);
478
514
  }
479
- } else if (pubsubMessage instanceof Subscribe) {
515
+ } else {
480
516
  if (!(await message.verify(true))) {
481
- logger.warn("Recieved message that did not verify Subscribe");
517
+ logger.warn("Recieved message that did not verify Unsubscribe");
482
518
  return false;
483
519
  }
484
520
 
485
- if (message.signatures.signatures.length === 0) {
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
- if (pubsubMessage.subscriptions.length === 0) {
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 subscriber = message.signatures.signatures[0].publicKey!;
501
- const subscriberKey = subscriber.hashcode(); // Assume first signature is the one who is signing
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
- this.initializePeer(subscriber);
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
- const changed: Subscription[] = [];
506
- pubsubMessage.subscriptions.forEach((subscription) => {
507
- const peers = this.topics.get(subscription.topic);
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
- // if no subscription data, or new subscription has data (and is newer) then overwrite it.
513
- // subscription where data is undefined is not intended to replace existing data
514
- const existingSubscription = peers.get(subscriberKey);
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?.data ||
531
- !equals(existingSubscription.data, subscription.data)
556
+ !existingSubscription ||
557
+ existingSubscription.timestamp < message.header.timetamp
532
558
  ) {
533
- changed.push(subscription);
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
- this.topicsToPeers.get(subscription.topic)?.add(subscriberKey);
538
- this.peerToTopic.get(subscriberKey)?.add(subscription.topic);
539
- });
540
- if (changed.length > 0) {
541
- this.dispatchEvent(
542
- new CustomEvent<SubscriptionEvent>("subscribe", {
543
- detail: new SubscriptionEvent(subscriber, changed)
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
- if (message.signatures.signatures.length === 0) {
557
- logger.warn("Recieved subscription message with no signers");
558
- return false;
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
- if (!this.subscriptionMessageIsLatest(message, pubsubMessage)) {
562
- logger.trace("Recieved old subscription message");
563
- return false;
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
- const changed: Subscription[] = [];
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
- for (const unsubscription of pubsubMessage.unsubscriptions) {
571
- const change = this.deletePeerFromTopic(
572
- unsubscription.topic,
573
- subscriberKey
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
- // Forward
589
- await this.relayMessage(from, message);
590
- } else if (pubsubMessage instanceof GetSubscribers) {
591
- if (!(await message.verify(true))) {
592
- logger.warn("Recieved message that did not verify GetSubscribers");
593
- return false;
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
- const subscriptionsToSend: Subscription[] = [];
597
- for (const topic of pubsubMessage.topics) {
598
- const subscription = this.subscriptions.get(topic);
599
- if (subscription) {
600
- subscriptionsToSend.push(new Subscription(topic, subscription.data));
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
- if (subscriptionsToSend.length > 0) {
605
- // respond
606
- if (!stream.isWritable) {
607
- try {
608
- await waitFor(() => stream.isWritable);
609
- } catch (error) {
610
- logger.warn(
611
- `Failed to respond to GetSubscribers request to ${from.toString()} stream is not writable`
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
- // Forward
630
- await this.relayMessage(from, message);
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.getSubscribers(topic);
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))