@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/lib/esm/index.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { logger as logFn } from "@peerbit/logger";
|
|
2
|
-
import { DataMessage } from "@peerbit/stream-interface";
|
|
2
|
+
import { AcknowledgeDelivery, AnyWhere, DataMessage, MessageHeader, SeekDelivery, SilentDelivery } from "@peerbit/stream-interface";
|
|
3
3
|
import { DirectStream } from "@peerbit/stream";
|
|
4
|
-
import { CodeError } from "@libp2p/interface
|
|
5
|
-
import { PubSubMessage, Subscribe, PubSubData, toUint8Array, Unsubscribe, GetSubscribers,
|
|
4
|
+
import { CodeError } from "@libp2p/interface";
|
|
5
|
+
import { PubSubMessage, Subscribe, PubSubData, toUint8Array, Unsubscribe, GetSubscribers, UnsubcriptionEvent, SubscriptionEvent, DataEvent, SubscriptionData, PublishEvent } from "@peerbit/pubsub-interface";
|
|
6
6
|
import { getPublicKeyFromPeerId, PublicSignKey } from "@peerbit/crypto";
|
|
7
|
-
import { CustomEvent } from "@libp2p/interface
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export const logger = logFn({ module: "direct-sub", level: "warn" });
|
|
7
|
+
import { CustomEvent } from "@libp2p/interface";
|
|
8
|
+
export const logger = logFn({ module: "lazysub", level: "warn" });
|
|
9
|
+
const logError = (e) => logger.error(e?.message);
|
|
11
10
|
export class DirectSub extends DirectStream {
|
|
12
11
|
topics; // topic -> peers --> Uint8Array subscription metadata (the latest received)
|
|
13
12
|
peerToTopic; // peer -> topics
|
|
@@ -15,7 +14,7 @@ export class DirectSub extends DirectStream {
|
|
|
15
14
|
subscriptions; // topic -> subscription ids
|
|
16
15
|
lastSubscriptionMessages = new Map();
|
|
17
16
|
constructor(components, props) {
|
|
18
|
-
super(components, ["
|
|
17
|
+
super(components, ["/lazysub/0.0.0"], props);
|
|
19
18
|
this.subscriptions = new Map();
|
|
20
19
|
this.topics = new Map();
|
|
21
20
|
this.topicsToPeers = new Map();
|
|
@@ -28,15 +27,6 @@ export class DirectSub extends DirectStream {
|
|
|
28
27
|
this.topicsToPeers.clear();
|
|
29
28
|
return super.stop();
|
|
30
29
|
}
|
|
31
|
-
async onPeerReachable(publicKey) {
|
|
32
|
-
// Aggregate subscribers for my topics through this new peer because if we don't do this we might end up with a situtation where
|
|
33
|
-
// 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
|
|
34
|
-
await this.requestSubscribers([...this.topics.keys()], publicKey);
|
|
35
|
-
return super.onPeerReachable(publicKey);
|
|
36
|
-
}
|
|
37
|
-
async onPeerDisconnected(peerId, conn) {
|
|
38
|
-
return super.onPeerDisconnected(peerId, conn);
|
|
39
|
-
}
|
|
40
30
|
initializeTopic(topic) {
|
|
41
31
|
this.topics.get(topic) || this.topics.set(topic, new Map());
|
|
42
32
|
this.topicsToPeers.get(topic) || this.topicsToPeers.set(topic, new Set());
|
|
@@ -48,43 +38,30 @@ export class DirectSub extends DirectStream {
|
|
|
48
38
|
/**
|
|
49
39
|
* Subscribes to a given topic.
|
|
50
40
|
*/
|
|
51
|
-
|
|
52
|
-
* @param topic,
|
|
53
|
-
* @param data, metadata associated with the subscription, shared with peers
|
|
54
|
-
*/
|
|
55
|
-
async subscribe(topic, options) {
|
|
41
|
+
async subscribe(topic) {
|
|
56
42
|
if (!this.started) {
|
|
57
43
|
throw new Error("Pubsub has not started");
|
|
58
44
|
}
|
|
59
|
-
topic = typeof topic === "string" ? [topic] : topic;
|
|
60
45
|
const newTopicsForTopicData = [];
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
this.subscriptions.set(t, {
|
|
74
|
-
counter: 1,
|
|
75
|
-
data: options?.data
|
|
76
|
-
});
|
|
77
|
-
newTopicsForTopicData.push(t);
|
|
78
|
-
this.listenForSubscribers(t);
|
|
79
|
-
}
|
|
46
|
+
const prev = this.subscriptions.get(topic);
|
|
47
|
+
if (prev) {
|
|
48
|
+
prev.counter += 1;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
this.subscriptions.set(topic, {
|
|
52
|
+
counter: 1
|
|
53
|
+
});
|
|
54
|
+
newTopicsForTopicData.push(topic);
|
|
55
|
+
this.listenForSubscribers(topic);
|
|
80
56
|
}
|
|
81
57
|
if (newTopicsForTopicData.length > 0) {
|
|
82
58
|
const message = new DataMessage({
|
|
83
59
|
data: toUint8Array(new Subscribe({
|
|
84
|
-
|
|
85
|
-
}).bytes())
|
|
60
|
+
topics: newTopicsForTopicData
|
|
61
|
+
}).bytes()),
|
|
62
|
+
header: new MessageHeader({ mode: new SeekDelivery({ redundancy: 2 }) })
|
|
86
63
|
});
|
|
87
|
-
await this.publishMessage(this.
|
|
64
|
+
await this.publishMessage(this.publicKey, await message.sign(this.sign));
|
|
88
65
|
}
|
|
89
66
|
}
|
|
90
67
|
/**
|
|
@@ -109,53 +86,40 @@ export class DirectSub extends DirectStream {
|
|
|
109
86
|
}
|
|
110
87
|
}
|
|
111
88
|
if (!subscriptions?.counter || options?.force) {
|
|
89
|
+
await this.publishMessage(this.publicKey, await new DataMessage({
|
|
90
|
+
header: new MessageHeader({
|
|
91
|
+
mode: new AnyWhere( /* {
|
|
92
|
+
redundancy: 2,
|
|
93
|
+
to: [...this.getPeersOnTopics([topic])]
|
|
94
|
+
} */)
|
|
95
|
+
}),
|
|
96
|
+
data: toUint8Array(new Unsubscribe({ topics: [topic] }).bytes())
|
|
97
|
+
}).sign(this.sign));
|
|
112
98
|
this.subscriptions.delete(topic);
|
|
113
99
|
this.topics.delete(topic);
|
|
114
100
|
this.topicsToPeers.delete(topic);
|
|
115
|
-
await this.publishMessage(this.components.peerId, await new DataMessage({
|
|
116
|
-
data: toUint8Array(new Unsubscribe({ topics: [topic] }).bytes())
|
|
117
|
-
}).sign(this.sign));
|
|
118
101
|
return true;
|
|
119
102
|
}
|
|
120
103
|
return false;
|
|
121
104
|
}
|
|
122
105
|
getSubscribers(topic) {
|
|
123
|
-
|
|
124
|
-
|
|
106
|
+
const remote = this.topics.get(topic.toString());
|
|
107
|
+
if (!remote) {
|
|
108
|
+
return undefined;
|
|
125
109
|
}
|
|
126
|
-
|
|
127
|
-
|
|
110
|
+
const ret = [];
|
|
111
|
+
for (const v of remote.values()) {
|
|
112
|
+
ret.push(v.publicKey);
|
|
128
113
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
getSubscribersWithData(topic, data, options) {
|
|
132
|
-
const map = this.topics.get(topic);
|
|
133
|
-
if (map) {
|
|
134
|
-
const results = [];
|
|
135
|
-
for (const [peer, info] of map.entries()) {
|
|
136
|
-
if (!info.data) {
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
if (options?.prefix) {
|
|
140
|
-
if (!startsWith(info.data, data)) {
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
else {
|
|
145
|
-
if (!equals(info.data, data)) {
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
results.push(peer);
|
|
150
|
-
}
|
|
151
|
-
return results;
|
|
114
|
+
if (this.subscriptions.get(topic)) {
|
|
115
|
+
ret.push(this.publicKey);
|
|
152
116
|
}
|
|
153
|
-
return;
|
|
117
|
+
return ret;
|
|
154
118
|
}
|
|
155
119
|
listenForSubscribers(topic) {
|
|
156
120
|
this.initializeTopic(topic);
|
|
157
121
|
}
|
|
158
|
-
async requestSubscribers(topic,
|
|
122
|
+
async requestSubscribers(topic, to) {
|
|
159
123
|
if (!this.started) {
|
|
160
124
|
throw new CodeError("not started yet", "ERR_NOT_STARTED_YET");
|
|
161
125
|
}
|
|
@@ -169,24 +133,29 @@ export class DirectSub extends DirectStream {
|
|
|
169
133
|
for (const topic of topics) {
|
|
170
134
|
this.listenForSubscribers(topic);
|
|
171
135
|
}
|
|
172
|
-
return this.publishMessage(this.
|
|
173
|
-
|
|
174
|
-
|
|
136
|
+
return this.publishMessage(this.publicKey, await new DataMessage({
|
|
137
|
+
data: toUint8Array(new GetSubscribers({ topics }).bytes()),
|
|
138
|
+
header: new MessageHeader({
|
|
139
|
+
mode: new SeekDelivery({
|
|
140
|
+
to: to ? [to.hashcode()] : [],
|
|
141
|
+
redundancy: 2
|
|
142
|
+
})
|
|
143
|
+
})
|
|
175
144
|
}).sign(this.sign));
|
|
176
145
|
}
|
|
177
|
-
|
|
178
|
-
const
|
|
146
|
+
getPeersOnTopics(topics) {
|
|
147
|
+
const newPeers = new Set();
|
|
179
148
|
if (topics?.length) {
|
|
180
149
|
for (const topic of topics) {
|
|
181
150
|
const peersOnTopic = this.topicsToPeers.get(topic.toString());
|
|
182
151
|
if (peersOnTopic) {
|
|
183
152
|
peersOnTopic.forEach((peer) => {
|
|
184
|
-
|
|
153
|
+
newPeers.add(peer);
|
|
185
154
|
});
|
|
186
155
|
}
|
|
187
156
|
}
|
|
188
157
|
}
|
|
189
|
-
return
|
|
158
|
+
return newPeers;
|
|
190
159
|
}
|
|
191
160
|
/* getStreamsWithTopics(topics: string[], otherPeers?: string[]): PeerStreams[] {
|
|
192
161
|
const peers = this.getNeighboursWithTopics(topics, otherPeers);
|
|
@@ -203,22 +172,28 @@ export class DirectSub extends DirectStream {
|
|
|
203
172
|
? x.hashcode()
|
|
204
173
|
: typeof x === "string"
|
|
205
174
|
? x
|
|
206
|
-
: getPublicKeyFromPeerId(x).hashcode()) || this.
|
|
175
|
+
: getPublicKeyFromPeerId(x).hashcode()) || this.getPeersOnTopics(topics);
|
|
207
176
|
// Embedd topic info before the data so that peers/relays can also use topic info to route messages efficiently
|
|
208
|
-
const dataMessage =
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
177
|
+
const dataMessage = data
|
|
178
|
+
? new PubSubData({
|
|
179
|
+
topics: topics.map((x) => x.toString()),
|
|
180
|
+
data,
|
|
181
|
+
strict: !!options?.to
|
|
182
|
+
})
|
|
183
|
+
: undefined;
|
|
184
|
+
const bytes = dataMessage?.bytes();
|
|
214
185
|
const message = await this.createMessage(bytes, { ...options, to: tos });
|
|
215
|
-
if (
|
|
216
|
-
|
|
217
|
-
detail: new
|
|
186
|
+
if (dataMessage) {
|
|
187
|
+
this.dispatchEvent(new CustomEvent("publish", {
|
|
188
|
+
detail: new PublishEvent({
|
|
189
|
+
client: options?.client,
|
|
190
|
+
data: dataMessage,
|
|
191
|
+
message
|
|
192
|
+
})
|
|
218
193
|
}));
|
|
219
194
|
}
|
|
220
195
|
// send to all the other peers
|
|
221
|
-
await this.publishMessage(this.
|
|
196
|
+
await this.publishMessage(this.publicKey, message, undefined);
|
|
222
197
|
return message.id;
|
|
223
198
|
}
|
|
224
199
|
deletePeerFromTopic(topic, publicKeyHash) {
|
|
@@ -235,28 +210,48 @@ export class DirectSub extends DirectStream {
|
|
|
235
210
|
this.topicsToPeers.get(topic)?.delete(publicKeyHash);
|
|
236
211
|
return change;
|
|
237
212
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
213
|
+
async onPeerReachable(publicKey) {
|
|
214
|
+
// Aggregate subscribers for my topics through this new peer because if we don't do this we might end up with a situtation where
|
|
215
|
+
// 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
|
|
216
|
+
/* await this.requestSubscribers([...this.topics.keys()], publicKey); */
|
|
217
|
+
const resp = super.onPeerReachable(publicKey);
|
|
218
|
+
const stream = this.peers.get(publicKey.hashcode());
|
|
219
|
+
if (stream && this.subscriptions.size > 0) {
|
|
220
|
+
// is new neighbour
|
|
221
|
+
// tell the peer about all topics we subscribe to
|
|
222
|
+
this.publishMessage(this.publicKey, await new DataMessage({
|
|
223
|
+
data: toUint8Array(new Subscribe({
|
|
224
|
+
topics: [...this.subscriptions.keys()]
|
|
225
|
+
}).bytes()),
|
|
226
|
+
header: new MessageHeader({
|
|
227
|
+
mode: new SeekDelivery({ redundancy: 2 })
|
|
228
|
+
})
|
|
229
|
+
}).sign(this.sign)),
|
|
230
|
+
[stream];
|
|
231
|
+
}
|
|
232
|
+
return resp;
|
|
233
|
+
}
|
|
234
|
+
onPeerUnreachable(publicKeyHash) {
|
|
235
|
+
super.onPeerUnreachable(publicKeyHash);
|
|
241
236
|
const peerTopics = this.peerToTopic.get(publicKeyHash);
|
|
242
237
|
const changed = [];
|
|
243
238
|
if (peerTopics) {
|
|
244
239
|
for (const topic of peerTopics) {
|
|
245
240
|
const change = this.deletePeerFromTopic(topic, publicKeyHash);
|
|
246
241
|
if (change) {
|
|
247
|
-
changed.push(
|
|
242
|
+
changed.push(topic);
|
|
248
243
|
}
|
|
249
244
|
}
|
|
250
245
|
}
|
|
251
246
|
this.lastSubscriptionMessages.delete(publicKeyHash);
|
|
252
247
|
if (changed.length > 0) {
|
|
253
248
|
this.dispatchEvent(new CustomEvent("unsubscribe", {
|
|
254
|
-
detail: new UnsubcriptionEvent(
|
|
249
|
+
detail: new UnsubcriptionEvent(this.peerKeyHashToPublicKey.get(publicKeyHash), changed)
|
|
255
250
|
}));
|
|
256
251
|
}
|
|
257
252
|
}
|
|
258
253
|
subscriptionMessageIsLatest(message, pubsubMessage) {
|
|
259
|
-
const subscriber = message.signatures.signatures[0].publicKey;
|
|
254
|
+
const subscriber = message.header.signatures.signatures[0].publicKey;
|
|
260
255
|
const subscriberKey = subscriber.hashcode(); // Assume first signature is the one who is signing
|
|
261
256
|
for (const topic of pubsubMessage.topics) {
|
|
262
257
|
const lastTimestamp = this.lastSubscriptionMessages
|
|
@@ -274,167 +269,204 @@ export class DirectSub extends DirectStream {
|
|
|
274
269
|
}
|
|
275
270
|
return true;
|
|
276
271
|
}
|
|
277
|
-
|
|
272
|
+
addPeersOnTopic(message, topics) {
|
|
273
|
+
const existingPeers = new Set(message.header.mode.to);
|
|
274
|
+
const allPeersOnTopic = this.getPeersOnTopics(topics);
|
|
275
|
+
for (const existing of existingPeers) {
|
|
276
|
+
allPeersOnTopic.add(existing);
|
|
277
|
+
}
|
|
278
|
+
allPeersOnTopic.delete(this.publicKeyHash);
|
|
279
|
+
message.header.mode.to = [...allPeersOnTopic];
|
|
280
|
+
}
|
|
281
|
+
async onDataMessage(from, stream, message, seenBefore) {
|
|
282
|
+
if (!message.data || message.data.length === 0) {
|
|
283
|
+
return super.onDataMessage(from, stream, message, seenBefore);
|
|
284
|
+
}
|
|
285
|
+
if (this.shouldIgnore(message, seenBefore)) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
278
288
|
const pubsubMessage = PubSubMessage.from(message.data);
|
|
279
289
|
if (pubsubMessage instanceof PubSubData) {
|
|
290
|
+
if (message.header.mode instanceof AnyWhere) {
|
|
291
|
+
throw new Error("Unexpected mode for PubSubData messages");
|
|
292
|
+
}
|
|
280
293
|
/**
|
|
281
294
|
* See if we know more subscribers of the message topics. If so, add aditional end receivers of the message
|
|
282
295
|
*/
|
|
283
|
-
let
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
+
let isForMe;
|
|
297
|
+
if (pubsubMessage.strict) {
|
|
298
|
+
isForMe =
|
|
299
|
+
!!pubsubMessage.topics.find((topic) => this.subscriptions.has(topic)) && !!message.header.mode.to?.find((x) => this.publicKeyHash === x);
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
isForMe =
|
|
303
|
+
!!pubsubMessage.topics.find((topic) => this.subscriptions.has(topic)) ||
|
|
304
|
+
(pubsubMessage.topics.length === 0 &&
|
|
305
|
+
!!message.header.mode.to?.find((x) => this.publicKeyHash === x));
|
|
306
|
+
}
|
|
307
|
+
if (isForMe) {
|
|
308
|
+
if ((await this.maybeVerifyMessage(message)) === false) {
|
|
309
|
+
logger.warn("Recieved message that did not verify PubSubData");
|
|
310
|
+
return false;
|
|
296
311
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
verified = await message.verify(this.signaturePolicy === "StictSign" ? true : false);
|
|
300
|
-
}
|
|
301
|
-
if (!verified) {
|
|
302
|
-
logger.warn("Recieved message that did not verify PubSubData");
|
|
303
|
-
return false;
|
|
304
|
-
}
|
|
312
|
+
await this.acknowledgeMessage(stream, message, seenBefore);
|
|
313
|
+
if (seenBefore === 0) {
|
|
305
314
|
this.dispatchEvent(new CustomEvent("data", {
|
|
306
|
-
detail: new DataEvent(
|
|
315
|
+
detail: new DataEvent({
|
|
316
|
+
data: pubsubMessage,
|
|
317
|
+
message
|
|
318
|
+
})
|
|
307
319
|
}));
|
|
308
320
|
}
|
|
309
321
|
}
|
|
322
|
+
if (message.header.mode.to) {
|
|
323
|
+
message.header.mode.to = message.header.mode.to.filter((x) => x !== this.publicKeyHash);
|
|
324
|
+
}
|
|
310
325
|
// Forward
|
|
311
326
|
if (!pubsubMessage.strict) {
|
|
312
|
-
|
|
313
|
-
newTos.delete(this.publicKeyHash);
|
|
314
|
-
message.to = [...newTos];
|
|
327
|
+
this.addPeersOnTopic(message, pubsubMessage.topics);
|
|
315
328
|
}
|
|
316
329
|
// Only relay if we got additional receivers
|
|
317
330
|
// or we are NOT subscribing ourselves (if we are not subscribing ourselves we are)
|
|
318
331
|
// If we are not subscribing ourselves, then we don't have enough information to "stop" message propagation here
|
|
319
|
-
if (message.to
|
|
320
|
-
|
|
321
|
-
|
|
332
|
+
if (message.header.mode.to?.length ||
|
|
333
|
+
0 > 0 ||
|
|
334
|
+
!pubsubMessage.topics.find((topic) => this.topics.has(topic)) ||
|
|
335
|
+
message.header.mode instanceof SeekDelivery) {
|
|
336
|
+
// DONT await this since it might introduce a dead-lock
|
|
337
|
+
this.relayMessage(from, message).catch(logError);
|
|
322
338
|
}
|
|
323
339
|
}
|
|
324
|
-
else
|
|
340
|
+
else {
|
|
325
341
|
if (!(await message.verify(true))) {
|
|
326
|
-
logger.warn("Recieved message that did not verify
|
|
342
|
+
logger.warn("Recieved message that did not verify Unsubscribe");
|
|
327
343
|
return false;
|
|
328
344
|
}
|
|
329
|
-
if (message.signatures.signatures.length === 0) {
|
|
345
|
+
if (message.header.signatures.signatures.length === 0) {
|
|
330
346
|
logger.warn("Recieved subscription message with no signers");
|
|
331
347
|
return false;
|
|
332
348
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
341
|
-
const subscriber = message.signatures.signatures[0].publicKey;
|
|
342
|
-
const subscriberKey = subscriber.hashcode(); // Assume first signature is the one who is signing
|
|
343
|
-
this.initializePeer(subscriber);
|
|
344
|
-
const changed = [];
|
|
345
|
-
pubsubMessage.subscriptions.forEach((subscription) => {
|
|
346
|
-
const peers = this.topics.get(subscription.topic);
|
|
347
|
-
if (peers == null) {
|
|
348
|
-
return;
|
|
349
|
+
await this.acknowledgeMessage(stream, message, seenBefore);
|
|
350
|
+
const sender = message.header.signatures.signatures[0].publicKey;
|
|
351
|
+
const senderKey = sender.hashcode(); // Assume first signature is the one who is signing
|
|
352
|
+
if (pubsubMessage instanceof Subscribe) {
|
|
353
|
+
if (pubsubMessage.topics.length === 0) {
|
|
354
|
+
logger.info("Recieved subscription message with no topics");
|
|
355
|
+
return false;
|
|
349
356
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
357
|
+
if (!this.subscriptionMessageIsLatest(message, pubsubMessage)) {
|
|
358
|
+
logger.trace("Recieved old subscription message");
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
this.initializePeer(sender);
|
|
362
|
+
const changed = [];
|
|
363
|
+
pubsubMessage.topics.forEach((topic) => {
|
|
364
|
+
const peers = this.topics.get(topic);
|
|
365
|
+
if (peers == null) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
// if no subscription data, or new subscription has data (and is newer) then overwrite it.
|
|
369
|
+
// subscription where data is undefined is not intended to replace existing data
|
|
370
|
+
const existingSubscription = peers.get(senderKey);
|
|
371
|
+
if (!existingSubscription ||
|
|
372
|
+
existingSubscription.timestamp < message.header.timetamp) {
|
|
373
|
+
peers.set(senderKey, new SubscriptionData({
|
|
374
|
+
timestamp: message.header.timetamp, // TODO update timestamps on all messages?
|
|
375
|
+
publicKey: sender
|
|
376
|
+
}));
|
|
377
|
+
if (!existingSubscription) {
|
|
378
|
+
changed.push(topic);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
this.topicsToPeers.get(topic)?.add(senderKey);
|
|
382
|
+
this.peerToTopic.get(senderKey)?.add(topic);
|
|
383
|
+
});
|
|
384
|
+
if (changed.length > 0) {
|
|
385
|
+
this.dispatchEvent(new CustomEvent("subscribe", {
|
|
386
|
+
detail: new SubscriptionEvent(sender, changed)
|
|
360
387
|
}));
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
388
|
+
// also send back a message telling the remote whether we are subsbscring
|
|
389
|
+
if (message.header.mode instanceof SeekDelivery) {
|
|
390
|
+
// only if Subscribe message is of 'seek' type we will respond with our subscriptions
|
|
391
|
+
const mySubscriptions = changed
|
|
392
|
+
.map((x) => {
|
|
393
|
+
const subscription = this.subscriptions.get(x);
|
|
394
|
+
return subscription ? x : undefined;
|
|
395
|
+
})
|
|
396
|
+
.filter((x) => !!x);
|
|
397
|
+
if (mySubscriptions.length > 0) {
|
|
398
|
+
const response = new DataMessage({
|
|
399
|
+
data: toUint8Array(new Subscribe({
|
|
400
|
+
topics: mySubscriptions
|
|
401
|
+
}).bytes()),
|
|
402
|
+
// needs to be Ack or Silent else we will run into a infite message loop
|
|
403
|
+
header: new MessageHeader({
|
|
404
|
+
mode: new AcknowledgeDelivery({
|
|
405
|
+
redundancy: 2,
|
|
406
|
+
to: [sender.hashcode()]
|
|
407
|
+
})
|
|
408
|
+
})
|
|
409
|
+
});
|
|
410
|
+
await this.publishMessage(this.publicKey, await response.sign(this.sign));
|
|
411
|
+
}
|
|
364
412
|
}
|
|
365
413
|
}
|
|
366
|
-
|
|
367
|
-
this
|
|
368
|
-
|
|
369
|
-
if (changed.length > 0) {
|
|
370
|
-
this.dispatchEvent(new CustomEvent("subscribe", {
|
|
371
|
-
detail: new SubscriptionEvent(subscriber, changed)
|
|
372
|
-
}));
|
|
373
|
-
}
|
|
374
|
-
// Forward
|
|
375
|
-
await this.relayMessage(from, message);
|
|
376
|
-
}
|
|
377
|
-
else if (pubsubMessage instanceof Unsubscribe) {
|
|
378
|
-
if (!(await message.verify(true))) {
|
|
379
|
-
logger.warn("Recieved message that did not verify Unsubscribe");
|
|
380
|
-
return false;
|
|
381
|
-
}
|
|
382
|
-
if (message.signatures.signatures.length === 0) {
|
|
383
|
-
logger.warn("Recieved subscription message with no signers");
|
|
384
|
-
return false;
|
|
385
|
-
}
|
|
386
|
-
if (!this.subscriptionMessageIsLatest(message, pubsubMessage)) {
|
|
387
|
-
logger.trace("Recieved old subscription message");
|
|
388
|
-
return false;
|
|
414
|
+
// Forward
|
|
415
|
+
// DONT await this since it might introduce a dead-lock
|
|
416
|
+
this.relayMessage(from, message).catch(logError);
|
|
389
417
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
const change = this.deletePeerFromTopic(unsubscription.topic, subscriberKey);
|
|
395
|
-
if (change) {
|
|
396
|
-
changed.push(new Subscription(unsubscription.topic, change.data));
|
|
418
|
+
else if (pubsubMessage instanceof Unsubscribe) {
|
|
419
|
+
if (!this.subscriptionMessageIsLatest(message, pubsubMessage)) {
|
|
420
|
+
logger.trace("Recieved old subscription message");
|
|
421
|
+
return false;
|
|
397
422
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
if (subscription) {
|
|
416
|
-
subscriptionsToSend.push(new Subscription(topic, subscription.data));
|
|
423
|
+
const changed = [];
|
|
424
|
+
for (const unsubscription of pubsubMessage.topics) {
|
|
425
|
+
const change = this.deletePeerFromTopic(unsubscription, senderKey);
|
|
426
|
+
if (change) {
|
|
427
|
+
changed.push(unsubscription);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
if (changed.length > 0) {
|
|
431
|
+
this.dispatchEvent(new CustomEvent("unsubscribe", {
|
|
432
|
+
detail: new UnsubcriptionEvent(sender, changed)
|
|
433
|
+
}));
|
|
434
|
+
}
|
|
435
|
+
// Forward
|
|
436
|
+
if (message.header.mode instanceof SeekDelivery ||
|
|
437
|
+
message.header.mode instanceof SilentDelivery ||
|
|
438
|
+
message.header.mode instanceof AcknowledgeDelivery) {
|
|
439
|
+
this.addPeersOnTopic(message, pubsubMessage.topics);
|
|
417
440
|
}
|
|
441
|
+
// DONT await this since it might introduce a dead-lock
|
|
442
|
+
this.relayMessage(from, message).catch(logError);
|
|
418
443
|
}
|
|
419
|
-
if (
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
catch (error) {
|
|
426
|
-
logger.warn(`Failed to respond to GetSubscribers request to ${from.toString()} stream is not writable`);
|
|
427
|
-
return false;
|
|
444
|
+
else if (pubsubMessage instanceof GetSubscribers) {
|
|
445
|
+
const subscriptionsToSend = [];
|
|
446
|
+
for (const topic of pubsubMessage.topics) {
|
|
447
|
+
const subscription = this.subscriptions.get(topic);
|
|
448
|
+
if (subscription) {
|
|
449
|
+
subscriptionsToSend.push(topic);
|
|
428
450
|
}
|
|
429
451
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
452
|
+
if (subscriptionsToSend.length > 0) {
|
|
453
|
+
// respond
|
|
454
|
+
this.publishMessage(this.publicKey, await new DataMessage({
|
|
455
|
+
data: toUint8Array(new Subscribe({
|
|
456
|
+
topics: subscriptionsToSend
|
|
457
|
+
}).bytes()),
|
|
458
|
+
header: new MessageHeader({
|
|
459
|
+
mode: new SilentDelivery({
|
|
460
|
+
redundancy: 2,
|
|
461
|
+
to: [sender.hashcode()]
|
|
462
|
+
})
|
|
463
|
+
})
|
|
464
|
+
}).sign(this.sign), [stream]); // send back to same stream
|
|
465
|
+
}
|
|
466
|
+
// Forward
|
|
467
|
+
// DONT await this since it might introduce a dead-lock
|
|
468
|
+
this.relayMessage(from, message).catch(logError);
|
|
435
469
|
}
|
|
436
|
-
// Forward
|
|
437
|
-
await this.relayMessage(from, message);
|
|
438
470
|
}
|
|
439
471
|
return true;
|
|
440
472
|
}
|
|
@@ -455,7 +487,7 @@ export const waitForSubscribers = async (libp2p, peersToWait, topic) => {
|
|
|
455
487
|
? id.hashcode()
|
|
456
488
|
: getPublicKeyFromPeerId(id).hashcode();
|
|
457
489
|
});
|
|
458
|
-
await libp2p.services.pubsub.requestSubscribers(topic);
|
|
490
|
+
// await libp2p.services.pubsub.requestSubscribers(topic);
|
|
459
491
|
return new Promise((resolve, reject) => {
|
|
460
492
|
let counter = 0;
|
|
461
493
|
const interval = setInterval(async () => {
|
|
@@ -465,7 +497,7 @@ export const waitForSubscribers = async (libp2p, peersToWait, topic) => {
|
|
|
465
497
|
reject(new Error("Failed to find expected subscribers for topic: " + topic));
|
|
466
498
|
}
|
|
467
499
|
try {
|
|
468
|
-
const peers = await libp2p.services.pubsub.
|
|
500
|
+
const peers = await libp2p.services.pubsub.topics.get(topic);
|
|
469
501
|
const hasAllPeers = peerIdsToWait
|
|
470
502
|
.map((e) => peers && peers.has(e))
|
|
471
503
|
.filter((e) => e === false).length === 0;
|