@libp2p/pubsub 1.2.3 → 1.2.7
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/dist/src/errors.d.ts +8 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +8 -0
- package/dist/src/errors.js.map +1 -1
- package/dist/src/index.d.ts +33 -30
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +192 -128
- package/dist/src/index.js.map +1 -1
- package/dist/src/message/rpc.d.ts +586 -175
- package/dist/src/message/rpc.js +1238 -67
- package/dist/src/message/sign.d.ts +1 -1
- package/dist/src/message/sign.d.ts.map +1 -1
- package/dist/src/message/sign.js +8 -14
- package/dist/src/message/sign.js.map +1 -1
- package/dist/src/peer-streams.d.ts +5 -5
- package/dist/src/peer-streams.d.ts.map +1 -1
- package/dist/src/peer-streams.js +1 -0
- package/dist/src/peer-streams.js.map +1 -1
- package/dist/src/utils.d.ts +6 -9
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +36 -35
- package/dist/src/utils.js.map +1 -1
- package/package.json +7 -10
- package/src/errors.ts +8 -0
- package/src/index.ts +236 -140
- package/src/message/rpc.d.ts +586 -175
- package/src/message/rpc.js +1238 -67
- package/src/message/rpc.proto +38 -4
- package/src/message/sign.ts +10 -16
- package/src/peer-streams.ts +6 -4
- package/src/utils.ts +35 -34
package/src/index.ts
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
import { logger } from '@libp2p/logger'
|
2
|
-
import { EventEmitter, CustomEvent } from '@libp2p/interfaces'
|
2
|
+
import { EventEmitter, CustomEvent, EventHandler } from '@libp2p/interfaces'
|
3
3
|
import errcode from 'err-code'
|
4
4
|
import { pipe } from 'it-pipe'
|
5
5
|
import Queue from 'p-queue'
|
6
6
|
import { Topology } from '@libp2p/topology'
|
7
7
|
import { codes } from './errors.js'
|
8
|
-
import {
|
9
|
-
import {
|
10
|
-
import * as utils from './utils.js'
|
8
|
+
import { PeerStreams as PeerStreamsImpl } from './peer-streams.js'
|
9
|
+
import { toMessage, ensureArray, randomSeqno, noSignMsgId, msgId, toRpcMessage } from './utils.js'
|
11
10
|
import {
|
12
11
|
signMessage,
|
13
12
|
verifySignature
|
@@ -15,9 +14,14 @@ import {
|
|
15
14
|
import type { PeerId } from '@libp2p/interfaces/peer-id'
|
16
15
|
import type { Registrar, IncomingStreamData } from '@libp2p/interfaces/registrar'
|
17
16
|
import type { Connection } from '@libp2p/interfaces/connection'
|
18
|
-
import type
|
19
|
-
import type { PubSub, Message, StrictNoSign, StrictSign, PubsubOptions, PubsubEvents } from '@libp2p/interfaces/pubsub'
|
17
|
+
import type { PubSub, Message, StrictNoSign, StrictSign, PubSubOptions, PubSubEvents, RPC, PeerStreams, RPCSubscription, RPCMessage } from '@libp2p/interfaces/pubsub'
|
20
18
|
import type { Logger } from '@libp2p/logger'
|
19
|
+
import { base58btc } from 'multiformats/bases/base58'
|
20
|
+
import { peerMap } from '@libp2p/peer-map'
|
21
|
+
import type { PeerMap } from '@libp2p/peer-map'
|
22
|
+
import { peerIdFromString } from '@libp2p/peer-id'
|
23
|
+
import type { IRPC } from './message/rpc.js'
|
24
|
+
import { RPC as RPCProto } from './message/rpc.js'
|
21
25
|
|
22
26
|
export interface TopicValidator { (topic: string, message: Message): Promise<void> }
|
23
27
|
|
@@ -25,7 +29,7 @@ export interface TopicValidator { (topic: string, message: Message): Promise<voi
|
|
25
29
|
* PubsubBaseProtocol handles the peers and connections logic for pubsub routers
|
26
30
|
* and specifies the API that pubsub routers should have.
|
27
31
|
*/
|
28
|
-
export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap &
|
32
|
+
export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubEvents> extends EventEmitter<EventMap & PubSubEvents> implements PubSub<EventMap & PubSubEvents> {
|
29
33
|
public peerId: PeerId
|
30
34
|
public started: boolean
|
31
35
|
/**
|
@@ -39,11 +43,11 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
39
43
|
/**
|
40
44
|
* Map of peer streams
|
41
45
|
*/
|
42
|
-
public peers:
|
46
|
+
public peers: PeerMap<PeerStreams>
|
43
47
|
/**
|
44
48
|
* The signature policy to follow by default
|
45
49
|
*/
|
46
|
-
public globalSignaturePolicy: StrictNoSign | StrictSign
|
50
|
+
public globalSignaturePolicy: typeof StrictNoSign | typeof StrictSign
|
47
51
|
/**
|
48
52
|
* If router can relay received messages, even if not subscribed
|
49
53
|
*/
|
@@ -61,20 +65,21 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
61
65
|
public topicValidators: Map<string, TopicValidator>
|
62
66
|
public queue: Queue
|
63
67
|
public registrar: Registrar
|
68
|
+
public multicodecs: string[]
|
64
69
|
|
65
70
|
protected log: Logger
|
66
|
-
protected multicodecs: string[]
|
67
71
|
protected _libp2p: any
|
68
72
|
private _registrarHandlerId: string | undefined
|
69
73
|
private _registrarTopologyId: string | undefined
|
70
74
|
|
71
|
-
constructor (props:
|
75
|
+
constructor (props: PubSubOptions) {
|
72
76
|
super()
|
73
77
|
|
74
78
|
const {
|
75
79
|
debugName = 'libp2p:pubsub',
|
76
80
|
multicodecs = [],
|
77
|
-
|
81
|
+
peerId,
|
82
|
+
registrar,
|
78
83
|
globalSignaturePolicy = 'StrictSign',
|
79
84
|
canRelayMessage = false,
|
80
85
|
emitSelf = false,
|
@@ -82,14 +87,13 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
82
87
|
} = props
|
83
88
|
|
84
89
|
this.log = logger(debugName)
|
85
|
-
this.multicodecs =
|
86
|
-
this.
|
87
|
-
this.
|
88
|
-
this.peerId = libp2p.peerId
|
90
|
+
this.multicodecs = ensureArray(multicodecs)
|
91
|
+
this.registrar = registrar
|
92
|
+
this.peerId = peerId
|
89
93
|
this.started = false
|
90
94
|
this.topics = new Map()
|
91
95
|
this.subscriptions = new Set()
|
92
|
-
this.peers =
|
96
|
+
this.peers = peerMap<PeerStreams>()
|
93
97
|
this.globalSignaturePolicy = globalSignaturePolicy === 'StrictNoSign' ? 'StrictNoSign' : 'StrictSign'
|
94
98
|
this.canRelayMessage = canRelayMessage
|
95
99
|
this.emitSelf = emitSelf
|
@@ -148,9 +152,11 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
148
152
|
}
|
149
153
|
|
150
154
|
this.log('stopping')
|
151
|
-
|
155
|
+
for (const peerStreams of this.peers.values()) {
|
156
|
+
peerStreams.close()
|
157
|
+
}
|
152
158
|
|
153
|
-
this.peers
|
159
|
+
this.peers.clear()
|
154
160
|
this.subscriptions = new Set()
|
155
161
|
this.started = false
|
156
162
|
this.log('stopped')
|
@@ -166,11 +172,10 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
166
172
|
protected _onIncomingStream (evt: CustomEvent<IncomingStreamData>) {
|
167
173
|
const { protocol, stream, connection } = evt.detail
|
168
174
|
const peerId = connection.remotePeer
|
169
|
-
const
|
170
|
-
const peer = this._addPeer(peerId, protocol)
|
175
|
+
const peer = this.addPeer(peerId, protocol)
|
171
176
|
const inboundStream = peer.attachInboundStream(stream)
|
172
177
|
|
173
|
-
this.
|
178
|
+
this.processMessages(peerId, inboundStream, peer)
|
174
179
|
.catch(err => this.log(err))
|
175
180
|
}
|
176
181
|
|
@@ -178,19 +183,18 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
178
183
|
* Registrar notifies an established connection with pubsub protocol
|
179
184
|
*/
|
180
185
|
protected async _onPeerConnected (peerId: PeerId, conn: Connection) {
|
181
|
-
|
182
|
-
this.log('connected', idB58Str)
|
186
|
+
this.log('connected %p', peerId)
|
183
187
|
|
184
188
|
try {
|
185
189
|
const { stream, protocol } = await conn.newStream(this.multicodecs)
|
186
|
-
const peer = this.
|
190
|
+
const peer = this.addPeer(peerId, protocol)
|
187
191
|
await peer.attachOutboundStream(stream)
|
188
192
|
} catch (err: any) {
|
189
193
|
this.log.error(err)
|
190
194
|
}
|
191
195
|
|
192
196
|
// Immediately send my own subscriptions to the newly established conn
|
193
|
-
this.
|
197
|
+
this.send(peerId, { subscriptions: Array.from(this.subscriptions).map(sub => sub.toString()), subscribe: true })
|
194
198
|
}
|
195
199
|
|
196
200
|
/**
|
@@ -206,9 +210,8 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
206
210
|
/**
|
207
211
|
* Notifies the router that a peer has been connected
|
208
212
|
*/
|
209
|
-
|
210
|
-
const
|
211
|
-
const existing = this.peers.get(id)
|
213
|
+
addPeer (peerId: PeerId, protocol: string): PeerStreams {
|
214
|
+
const existing = this.peers.get(peerId)
|
212
215
|
|
213
216
|
// If peer streams already exists, do nothing
|
214
217
|
if (existing != null) {
|
@@ -216,14 +219,14 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
216
219
|
}
|
217
220
|
|
218
221
|
// else create a new peer streams
|
219
|
-
this.log('new peer',
|
222
|
+
this.log('new peer %p', peerId)
|
220
223
|
|
221
|
-
const peerStreams = new
|
224
|
+
const peerStreams: PeerStreams = new PeerStreamsImpl({
|
222
225
|
id: peerId,
|
223
226
|
protocol
|
224
227
|
})
|
225
228
|
|
226
|
-
this.peers.set(
|
229
|
+
this.peers.set(peerId, peerStreams)
|
227
230
|
peerStreams.addEventListener('close', () => this._removePeer(peerId), {
|
228
231
|
once: true
|
229
232
|
})
|
@@ -236,7 +239,7 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
236
239
|
*/
|
237
240
|
protected _removePeer (peerId: PeerId) {
|
238
241
|
const id = peerId.toString()
|
239
|
-
const peerStreams = this.peers.get(
|
242
|
+
const peerStreams = this.peers.get(peerId)
|
240
243
|
if (peerStreams == null) {
|
241
244
|
return
|
242
245
|
}
|
@@ -245,8 +248,8 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
245
248
|
peerStreams.close()
|
246
249
|
|
247
250
|
// delete peer streams
|
248
|
-
this.log('delete peer',
|
249
|
-
this.peers.delete(
|
251
|
+
this.log('delete peer %p', peerId)
|
252
|
+
this.peers.delete(peerId)
|
250
253
|
|
251
254
|
// remove peer from topics map
|
252
255
|
for (const peers of this.topics.values()) {
|
@@ -261,20 +264,42 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
261
264
|
/**
|
262
265
|
* Responsible for processing each RPC message received by other peers.
|
263
266
|
*/
|
264
|
-
async
|
267
|
+
async processMessages (peerId: PeerId, stream: AsyncIterable<Uint8Array>, peerStreams: PeerStreams) {
|
265
268
|
try {
|
266
269
|
await pipe(
|
267
270
|
stream,
|
268
271
|
async (source) => {
|
269
272
|
for await (const data of source) {
|
270
|
-
const
|
271
|
-
const
|
272
|
-
|
273
|
-
|
273
|
+
const rpcMsg = this.decodeRpc(data)
|
274
|
+
const messages: RPCMessage[] = []
|
275
|
+
|
276
|
+
for (const msg of (rpcMsg.messages ?? [])) {
|
277
|
+
if (msg.from == null || msg.data == null || msg.topic == null) {
|
278
|
+
this.log('message from %p was missing from, data or topic fields, dropping', peerId)
|
279
|
+
continue
|
280
|
+
}
|
281
|
+
|
282
|
+
messages.push({
|
283
|
+
from: msg.from,
|
284
|
+
data: msg.data,
|
285
|
+
topic: msg.topic,
|
286
|
+
seqno: msg.seqno ?? undefined,
|
287
|
+
signature: msg.signature ?? undefined,
|
288
|
+
key: msg.key ?? undefined
|
289
|
+
})
|
290
|
+
}
|
291
|
+
|
292
|
+
// Since processRpc may be overridden entirely in unsafe ways,
|
274
293
|
// the simplest/safest option here is to wrap in a function and capture all errors
|
275
294
|
// to prevent a top-level unhandled exception
|
276
295
|
// This processing of rpc messages should happen without awaiting full validation/execution of prior messages
|
277
|
-
this.
|
296
|
+
this.processRpc(peerId, peerStreams, {
|
297
|
+
subscriptions: (rpcMsg.subscriptions).map(sub => ({
|
298
|
+
subscribe: Boolean(sub.subscribe),
|
299
|
+
topic: sub.topic ?? ''
|
300
|
+
})),
|
301
|
+
messages
|
302
|
+
})
|
278
303
|
.catch(err => this.log(err))
|
279
304
|
}
|
280
305
|
}
|
@@ -287,54 +312,55 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
287
312
|
/**
|
288
313
|
* Handles an rpc request from a peer
|
289
314
|
*/
|
290
|
-
async
|
291
|
-
this.
|
292
|
-
|
293
|
-
|
315
|
+
async processRpc (from: PeerId, peerStreams: PeerStreams, rpc: RPC) {
|
316
|
+
if (!this.acceptFrom(from)) {
|
317
|
+
this.log('received message from unacceptable peer %p', from)
|
318
|
+
return
|
319
|
+
}
|
320
|
+
|
321
|
+
this.log('rpc from %p', from)
|
322
|
+
|
323
|
+
const { subscriptions, messages } = rpc
|
324
|
+
|
325
|
+
if (subscriptions.length > 0) {
|
326
|
+
this.log('subscription update from %p', from)
|
294
327
|
|
295
|
-
if (subs.length > 0) {
|
296
328
|
// update peer subscriptions
|
297
|
-
|
298
|
-
this.
|
329
|
+
subscriptions.forEach((subOpt) => {
|
330
|
+
this.processRpcSubOpt(from, subOpt)
|
299
331
|
})
|
300
|
-
this.dispatchEvent(new CustomEvent('pubsub:subscription-change', {
|
301
|
-
detail: { peerId: peerStreams.id, subscriptions: subs }
|
302
|
-
}))
|
303
|
-
}
|
304
332
|
|
305
|
-
|
306
|
-
|
307
|
-
|
333
|
+
super.dispatchEvent(new CustomEvent('pubsub:subscription-change', {
|
334
|
+
detail: { peerId: peerStreams.id, subscriptions }
|
335
|
+
}))
|
308
336
|
}
|
309
337
|
|
310
|
-
if (
|
311
|
-
this.
|
312
|
-
const topics = message.topicIDs != null ? message.topicIDs : []
|
313
|
-
const hasSubscription = topics.some((topic) => this.subscriptions.has(topic))
|
338
|
+
if (messages.length > 0) {
|
339
|
+
this.log('messages from %p', from)
|
314
340
|
|
315
|
-
|
341
|
+
this.queue.addAll(messages.map(message => async () => {
|
342
|
+
if (!this.subscriptions.has(message.topic) && !this.canRelayMessage) {
|
316
343
|
this.log('received message we didn\'t subscribe to. Dropping.')
|
317
344
|
return
|
318
345
|
}
|
319
346
|
|
320
347
|
try {
|
321
|
-
const msg =
|
348
|
+
const msg = await toMessage(message)
|
322
349
|
|
323
|
-
await this.
|
350
|
+
await this.processMessage(from, msg)
|
324
351
|
} catch (err: any) {
|
325
352
|
this.log.error(err)
|
326
353
|
}
|
327
354
|
}))
|
328
355
|
.catch(err => this.log(err))
|
329
356
|
}
|
330
|
-
return true
|
331
357
|
}
|
332
358
|
|
333
359
|
/**
|
334
360
|
* Handles a subscription change from a peer
|
335
361
|
*/
|
336
|
-
|
337
|
-
const t = subOpt.
|
362
|
+
processRpcSubOpt (id: PeerId, subOpt: RPCSubscription) {
|
363
|
+
const t = subOpt.topic
|
338
364
|
|
339
365
|
if (t == null) {
|
340
366
|
return
|
@@ -346,20 +372,20 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
346
372
|
this.topics.set(t, topicSet)
|
347
373
|
}
|
348
374
|
|
349
|
-
if (subOpt.subscribe
|
375
|
+
if (subOpt.subscribe) {
|
350
376
|
// subscribe peer to new topic
|
351
|
-
topicSet.add(id)
|
377
|
+
topicSet.add(id.toString())
|
352
378
|
} else {
|
353
379
|
// unsubscribe from existing topic
|
354
|
-
topicSet.delete(id)
|
380
|
+
topicSet.delete(id.toString())
|
355
381
|
}
|
356
382
|
}
|
357
383
|
|
358
384
|
/**
|
359
385
|
* Handles an message from a peer
|
360
386
|
*/
|
361
|
-
async
|
362
|
-
if (
|
387
|
+
async processMessage (from: PeerId, msg: Message) {
|
388
|
+
if (this.peerId.equals(from) && !this.emitSelf) {
|
363
389
|
return
|
364
390
|
}
|
365
391
|
|
@@ -371,23 +397,17 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
371
397
|
return
|
372
398
|
}
|
373
399
|
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
return await this._publish(utils.normalizeOutRpcMessage(msg))
|
378
|
-
}
|
400
|
+
if (this.subscriptions.has(msg.topic)) {
|
401
|
+
const isFromSelf = this.peerId.equals(from)
|
379
402
|
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
_emitMessage (message: Message) {
|
384
|
-
message.topicIDs.forEach((topic) => {
|
385
|
-
if (this.subscriptions.has(topic)) {
|
386
|
-
this.dispatchEvent(new CustomEvent(topic, {
|
387
|
-
detail: message
|
403
|
+
if (!isFromSelf || this.emitSelf) {
|
404
|
+
super.dispatchEvent(new CustomEvent(msg.topic, {
|
405
|
+
detail: msg
|
388
406
|
}))
|
389
407
|
}
|
390
|
-
}
|
408
|
+
}
|
409
|
+
|
410
|
+
await this.publishMessage(from, msg)
|
391
411
|
}
|
392
412
|
|
393
413
|
/**
|
@@ -398,10 +418,17 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
398
418
|
const signaturePolicy = this.globalSignaturePolicy
|
399
419
|
switch (signaturePolicy) {
|
400
420
|
case 'StrictSign':
|
401
|
-
|
402
|
-
|
421
|
+
if (msg.seqno == null) {
|
422
|
+
throw errcode(new Error('Need seqno when signature policy is StrictSign but it was missing'), codes.ERR_MISSING_SEQNO)
|
423
|
+
}
|
424
|
+
|
425
|
+
if (msg.key == null) {
|
426
|
+
throw errcode(new Error('Need key when signature policy is StrictSign but it was missing'), codes.ERR_MISSING_KEY)
|
427
|
+
}
|
428
|
+
|
429
|
+
return msgId(msg.key, msg.seqno)
|
403
430
|
case 'StrictNoSign':
|
404
|
-
return
|
431
|
+
return noSignMsgId(msg.data)
|
405
432
|
default:
|
406
433
|
throw errcode(new Error('Cannot get message id: unhandled signature policy'), codes.ERR_UNHANDLED_SIGNATURE_POLICY)
|
407
434
|
}
|
@@ -411,7 +438,7 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
411
438
|
* Whether to accept a message from a peer
|
412
439
|
* Override to create a graylist
|
413
440
|
*/
|
414
|
-
|
441
|
+
acceptFrom (id: PeerId) {
|
415
442
|
return true
|
416
443
|
}
|
417
444
|
|
@@ -419,39 +446,44 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
419
446
|
* Decode Uint8Array into an RPC object.
|
420
447
|
* This can be override to use a custom router protobuf.
|
421
448
|
*/
|
422
|
-
|
423
|
-
return
|
449
|
+
decodeRpc (bytes: Uint8Array) {
|
450
|
+
return RPCProto.decode(bytes)
|
424
451
|
}
|
425
452
|
|
426
453
|
/**
|
427
454
|
* Encode RPC object into a Uint8Array.
|
428
455
|
* This can be override to use a custom router protobuf.
|
429
456
|
*/
|
430
|
-
|
431
|
-
return
|
457
|
+
encodeRpc (rpc: IRPC) {
|
458
|
+
return RPCProto.encode(rpc).finish()
|
432
459
|
}
|
433
460
|
|
434
461
|
/**
|
435
462
|
* Send an rpc object to a peer
|
436
463
|
*/
|
437
|
-
|
438
|
-
const
|
439
|
-
if ((peerStreams == null) || !peerStreams.isWritable) {
|
440
|
-
const msg = `Cannot send RPC to ${id} as there is no open stream to it available`
|
464
|
+
send (peer: PeerId, data: { messages?: Message[], subscriptions?: string[], subscribe?: boolean }) {
|
465
|
+
const { messages, subscriptions, subscribe } = data
|
441
466
|
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
467
|
+
return this.sendRpc(peer, {
|
468
|
+
subscriptions: (subscriptions ?? []).map(str => ({ topic: str, subscribe: Boolean(subscribe) })),
|
469
|
+
messages: (messages ?? []).map(toRpcMessage)
|
470
|
+
})
|
446
471
|
}
|
447
472
|
|
448
473
|
/**
|
449
|
-
* Send
|
474
|
+
* Send an rpc object to a peer
|
450
475
|
*/
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
476
|
+
sendRpc (peer: PeerId, rpc: IRPC) {
|
477
|
+
const peerStreams = this.peers.get(peer)
|
478
|
+
|
479
|
+
if (peerStreams == null || !peerStreams.isWritable) {
|
480
|
+
const msg = `Cannot send RPC to ${peer.toString(base58btc)} as there is no open stream to it available`
|
481
|
+
|
482
|
+
this.log.error(msg)
|
483
|
+
return
|
484
|
+
}
|
485
|
+
|
486
|
+
peerStreams.write(this.encodeRpc(rpc))
|
455
487
|
}
|
456
488
|
|
457
489
|
/**
|
@@ -462,9 +494,6 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
462
494
|
const signaturePolicy = this.globalSignaturePolicy
|
463
495
|
switch (signaturePolicy) {
|
464
496
|
case 'StrictNoSign':
|
465
|
-
if (message.from != null) {
|
466
|
-
throw errcode(new Error('StrictNoSigning: from should not be present'), codes.ERR_UNEXPECTED_FROM)
|
467
|
-
}
|
468
497
|
if (message.signature != null) {
|
469
498
|
throw errcode(new Error('StrictNoSigning: signature should not be present'), codes.ERR_UNEXPECTED_SIGNATURE)
|
470
499
|
}
|
@@ -490,11 +519,10 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
490
519
|
throw errcode(new Error('Cannot validate message: unhandled signature policy'), codes.ERR_UNHANDLED_SIGNATURE_POLICY)
|
491
520
|
}
|
492
521
|
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
}
|
522
|
+
const validatorFn = this.topicValidators.get(message.topic)
|
523
|
+
|
524
|
+
if (validatorFn != null) {
|
525
|
+
await validatorFn(message.topic, message)
|
498
526
|
}
|
499
527
|
}
|
500
528
|
|
@@ -502,12 +530,11 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
502
530
|
* Normalizes the message and signs it, if signing is enabled.
|
503
531
|
* Should be used by the routers to create the message to send.
|
504
532
|
*/
|
505
|
-
|
533
|
+
async buildMessage (message: Message) {
|
506
534
|
const signaturePolicy = this.globalSignaturePolicy
|
507
535
|
switch (signaturePolicy) {
|
508
536
|
case 'StrictSign':
|
509
|
-
message.
|
510
|
-
message.seqno = utils.randomSeqno()
|
537
|
+
message.seqno = randomSeqno()
|
511
538
|
return await signMessage(this.peerId, message)
|
512
539
|
case 'StrictNoSign':
|
513
540
|
return await Promise.resolve(message)
|
@@ -530,48 +557,67 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
530
557
|
throw errcode(new Error('topic is required'), 'ERR_NOT_VALID_TOPIC')
|
531
558
|
}
|
532
559
|
|
533
|
-
const peersInTopic = this.topics.get(topic)
|
560
|
+
const peersInTopic = this.topics.get(topic.toString())
|
534
561
|
|
535
562
|
if (peersInTopic == null) {
|
536
563
|
return []
|
537
564
|
}
|
538
565
|
|
539
|
-
return Array.from(peersInTopic)
|
566
|
+
return Array.from(peersInTopic).map(str => peerIdFromString(str))
|
540
567
|
}
|
541
568
|
|
542
569
|
/**
|
543
570
|
* Publishes messages to all subscribed peers
|
544
571
|
*/
|
545
|
-
|
572
|
+
dispatchEvent (event: CustomEvent): boolean {
|
546
573
|
if (!this.started) {
|
547
574
|
throw new Error('Pubsub has not started')
|
548
575
|
}
|
549
576
|
|
550
|
-
|
577
|
+
const topic = event.type
|
578
|
+
let message: Message = event.detail
|
551
579
|
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
580
|
+
if (message instanceof Uint8Array) {
|
581
|
+
message = {
|
582
|
+
from: this.peerId,
|
583
|
+
topic,
|
584
|
+
data: message
|
585
|
+
}
|
557
586
|
}
|
558
587
|
|
559
|
-
|
560
|
-
|
561
|
-
|
588
|
+
this.log('publish', topic, message)
|
589
|
+
|
590
|
+
Promise.resolve().then(async () => {
|
591
|
+
message = await this.buildMessage(message)
|
592
|
+
|
593
|
+
// dispatch the event if we are interested
|
594
|
+
if (this.emitSelf) {
|
595
|
+
if (this.subscriptions.has(topic)) {
|
596
|
+
super.dispatchEvent(new CustomEvent(topic, {
|
597
|
+
detail: message
|
598
|
+
}))
|
599
|
+
|
600
|
+
if (this.listenerCount(topic) === 0) {
|
601
|
+
this.unsubscribe(topic)
|
602
|
+
}
|
603
|
+
}
|
604
|
+
}
|
562
605
|
|
563
|
-
|
564
|
-
|
606
|
+
// send to all the other peers
|
607
|
+
await this.publishMessage(this.peerId, message)
|
608
|
+
})
|
609
|
+
.catch(err => {
|
610
|
+
this.log.error(err)
|
611
|
+
})
|
565
612
|
|
566
|
-
|
567
|
-
await this._publish(msg)
|
613
|
+
return true
|
568
614
|
}
|
569
615
|
|
570
616
|
/**
|
571
617
|
* Overriding the implementation of publish should handle the appropriate algorithms for the publish/subscriber implementation.
|
572
618
|
* For example, a Floodsub implementation might simply publish each message to each topic for every peer
|
573
619
|
*/
|
574
|
-
abstract
|
620
|
+
abstract publishMessage (peerId: PeerId, message: Message): Promise<void>
|
575
621
|
|
576
622
|
/**
|
577
623
|
* Subscribes to a given topic.
|
@@ -581,23 +627,49 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
581
627
|
throw new Error('Pubsub has not started')
|
582
628
|
}
|
583
629
|
|
584
|
-
|
585
|
-
|
586
|
-
|
630
|
+
const topicStr = topic.toString()
|
631
|
+
|
632
|
+
if (topicStr === 'pubsub:subscription-change') {
|
633
|
+
return
|
634
|
+
}
|
635
|
+
|
636
|
+
if (!this.subscriptions.has(topicStr)) {
|
637
|
+
this.subscriptions.add(topicStr)
|
638
|
+
|
639
|
+
for (const peerId of this.peers.keys()) {
|
640
|
+
this.send(peerId, { subscriptions: [topicStr], subscribe: true })
|
641
|
+
}
|
587
642
|
}
|
588
643
|
}
|
589
644
|
|
590
645
|
/**
|
591
|
-
* Unsubscribe from the given topic
|
646
|
+
* Unsubscribe from the given topic
|
592
647
|
*/
|
593
648
|
unsubscribe (topic: string) {
|
594
649
|
if (!this.started) {
|
595
650
|
throw new Error('Pubsub is not started')
|
596
651
|
}
|
597
652
|
|
598
|
-
|
599
|
-
|
600
|
-
|
653
|
+
// @ts-expect-error topic should be a key of the event map
|
654
|
+
super.removeEventListener(topic)
|
655
|
+
|
656
|
+
const topicStr = topic.toString()
|
657
|
+
|
658
|
+
if (topicStr === 'pubsub:subscription-change') {
|
659
|
+
return
|
660
|
+
}
|
661
|
+
|
662
|
+
const wasSubscribed = this.subscriptions.has(topicStr)
|
663
|
+
const listeners = this.listenerCount(topicStr)
|
664
|
+
|
665
|
+
this.log('unsubscribe from %s - am subscribed %s, listeners %d', topic, wasSubscribed, listeners)
|
666
|
+
|
667
|
+
if (wasSubscribed && listeners === 0) {
|
668
|
+
this.subscriptions.delete(topicStr)
|
669
|
+
|
670
|
+
for (const peerId of this.peers.keys()) {
|
671
|
+
this.send(peerId, { subscriptions: [topicStr], subscribe: false })
|
672
|
+
}
|
601
673
|
}
|
602
674
|
}
|
603
675
|
|
@@ -611,4 +683,28 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
611
683
|
|
612
684
|
return Array.from(this.subscriptions)
|
613
685
|
}
|
686
|
+
|
687
|
+
getPeers () {
|
688
|
+
if (!this.started) {
|
689
|
+
throw new Error('Pubsub is not started')
|
690
|
+
}
|
691
|
+
|
692
|
+
return Array.from(this.peers.keys())
|
693
|
+
}
|
694
|
+
|
695
|
+
addEventListener<U extends keyof EventMap> (type: U, callback: EventHandler<EventMap[U]>, options?: AddEventListenerOptions | boolean) {
|
696
|
+
this.subscribe(type.toString())
|
697
|
+
|
698
|
+
super.addEventListener(type, callback, options)
|
699
|
+
}
|
700
|
+
|
701
|
+
removeEventListener<U extends keyof EventMap> (type: U, callback: EventHandler<EventMap[U]> | undefined, options?: EventListenerOptions | boolean) {
|
702
|
+
super.removeEventListener(type, callback, options)
|
703
|
+
|
704
|
+
const topicStr = type.toString()
|
705
|
+
|
706
|
+
if (this.listenerCount(topicStr) === 0) {
|
707
|
+
this.unsubscribe(topicStr)
|
708
|
+
}
|
709
|
+
}
|
614
710
|
}
|