@libp2p/pubsub 1.2.1 → 1.2.5

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
@@ -3,22 +3,25 @@ import { EventEmitter, CustomEvent } 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
- import { MulticodecTopology } from '@libp2p/topology/multicodec-topology'
6
+ import { Topology } from '@libp2p/topology'
7
7
  import { codes } from './errors.js'
8
- import { RPC, IRPC } from './message/rpc.js'
9
- import { PeerStreams } from './peer-streams.js'
10
- import * as utils from './utils.js'
8
+ import { PeerStreams as PeerStreamsImpl } from './peer-streams.js'
9
+ import { toRpcMessage, toMessage, ensureArray, randomSeqno, noSignMsgId, msgId } from './utils.js'
11
10
  import {
12
11
  signMessage,
13
12
  verifySignature
14
13
  } from './message/sign.js'
15
14
  import type { PeerId } from '@libp2p/interfaces/peer-id'
16
- import type { Registrar, IncomingStreamEvent } from '@libp2p/interfaces/registrar'
15
+ import type { Registrar, IncomingStreamData } from '@libp2p/interfaces/registrar'
17
16
  import type { Connection } from '@libp2p/interfaces/connection'
18
- import type BufferList from 'bl'
19
- import type { PubSub, Message, StrictNoSign, StrictSign, PubsubOptions, PubsubEvents } from '@libp2p/interfaces/pubsub'
20
- import type { Startable } from '@libp2p/interfaces'
17
+ import type { PubSub, Message, StrictNoSign, StrictSign, PubSubOptions, PubSubEvents, RPCMessage, RPC, PeerStreams, RPCSubscription } from '@libp2p/interfaces/pubsub'
21
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'
22
25
 
23
26
  export interface TopicValidator { (topic: string, message: Message): Promise<void> }
24
27
 
@@ -26,7 +29,7 @@ export interface TopicValidator { (topic: string, message: Message): Promise<voi
26
29
  * PubsubBaseProtocol handles the peers and connections logic for pubsub routers
27
30
  * and specifies the API that pubsub routers should have.
28
31
  */
29
- export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap & PubsubEvents> implements PubSub<EventMap & PubsubEvents>, Startable {
32
+ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap & PubSubEvents> implements PubSub<EventMap & PubSubEvents> {
30
33
  public peerId: PeerId
31
34
  public started: boolean
32
35
  /**
@@ -40,11 +43,11 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
40
43
  /**
41
44
  * Map of peer streams
42
45
  */
43
- public peers: Map<string, PeerStreams>
46
+ public peers: PeerMap<PeerStreams>
44
47
  /**
45
48
  * The signature policy to follow by default
46
49
  */
47
- public globalSignaturePolicy: StrictNoSign | StrictSign
50
+ public globalSignaturePolicy: typeof StrictNoSign | typeof StrictSign
48
51
  /**
49
52
  * If router can relay received messages, even if not subscribed
50
53
  */
@@ -62,19 +65,21 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
62
65
  public topicValidators: Map<string, TopicValidator>
63
66
  public queue: Queue
64
67
  public registrar: Registrar
68
+ public multicodecs: string[]
65
69
 
66
70
  protected log: Logger
67
- protected multicodecs: string[]
68
71
  protected _libp2p: any
69
- private _registrarId: string | undefined
72
+ private _registrarHandlerId: string | undefined
73
+ private _registrarTopologyId: string | undefined
70
74
 
71
- constructor (props: PubsubOptions) {
75
+ constructor (props: PubSubOptions) {
72
76
  super()
73
77
 
74
78
  const {
75
79
  debugName = 'libp2p:pubsub',
76
80
  multicodecs = [],
77
- libp2p = null,
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 = utils.ensureArray(multicodecs)
86
- this._libp2p = libp2p
87
- this.registrar = libp2p.registrar
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 = new Map()
96
+ this.peers = peerMap<PeerStreams>()
93
97
  this.globalSignaturePolicy = globalSignaturePolicy === 'StrictNoSign' ? 'StrictNoSign' : 'StrictSign'
94
98
  this.canRelayMessage = canRelayMessage
95
99
  this.emitSelf = emitSelf
@@ -108,7 +112,7 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
108
112
  *
109
113
  * @returns {void}
110
114
  */
111
- start () {
115
+ async start () {
112
116
  if (this.started) {
113
117
  return
114
118
  }
@@ -117,20 +121,15 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
117
121
 
118
122
  // Incoming streams
119
123
  // Called after a peer dials us
120
- this.registrar.handle(this.multicodecs, this._onIncomingStream)
124
+ this._registrarHandlerId = await this.registrar.handle(this.multicodecs, this._onIncomingStream)
121
125
 
122
126
  // register protocol with topology
123
127
  // Topology callbacks called on connection manager changes
124
- const topology = new MulticodecTopology({
125
- peerStore: this._libp2p.peerStore,
126
- connectionManager: this._libp2p.connectionManager,
127
- multicodecs: this.multicodecs,
128
- handlers: {
129
- onConnect: this._onPeerConnected,
130
- onDisconnect: this._onPeerDisconnected
131
- }
128
+ const topology = new Topology({
129
+ onConnect: this._onPeerConnected,
130
+ onDisconnect: this._onPeerDisconnected
132
131
  })
133
- this._registrarId = this.registrar.register(topology)
132
+ this._registrarTopologyId = this.registrar.register(this.multicodecs, topology)
134
133
 
135
134
  this.log('started')
136
135
  this.started = true
@@ -138,23 +137,26 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
138
137
 
139
138
  /**
140
139
  * Unregister the pubsub protocol and the streams with other peers will be closed.
141
- *
142
- * @returns {void}
143
140
  */
144
- stop () {
141
+ async stop () {
145
142
  if (!this.started) {
146
143
  return
147
144
  }
148
145
 
149
146
  // unregister protocol and handlers
150
- if (this._registrarId != null) {
151
- this.registrar.unregister(this._registrarId)
147
+ if (this._registrarTopologyId != null) {
148
+ this.registrar.unregister(this._registrarTopologyId)
149
+ }
150
+ if (this._registrarHandlerId != null) {
151
+ await this.registrar.unhandle(this._registrarHandlerId)
152
152
  }
153
153
 
154
154
  this.log('stopping')
155
- this.peers.forEach((peerStreams) => peerStreams.close())
155
+ for (const peerStreams of this.peers.values()) {
156
+ peerStreams.close()
157
+ }
156
158
 
157
- this.peers = new Map()
159
+ this.peers.clear()
158
160
  this.subscriptions = new Set()
159
161
  this.started = false
160
162
  this.log('stopped')
@@ -167,13 +169,13 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
167
169
  /**
168
170
  * On an inbound stream opened
169
171
  */
170
- protected _onIncomingStream ({ protocol, stream, connection }: IncomingStreamEvent) {
172
+ protected _onIncomingStream (evt: CustomEvent<IncomingStreamData>) {
173
+ const { protocol, stream, connection } = evt.detail
171
174
  const peerId = connection.remotePeer
172
- const idB58Str = peerId.toString()
173
175
  const peer = this._addPeer(peerId, protocol)
174
176
  const inboundStream = peer.attachInboundStream(stream)
175
177
 
176
- this._processMessages(idB58Str, inboundStream, peer)
178
+ this._processMessages(peerId, inboundStream, peer)
177
179
  .catch(err => this.log(err))
178
180
  }
179
181
 
@@ -181,8 +183,7 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
181
183
  * Registrar notifies an established connection with pubsub protocol
182
184
  */
183
185
  protected async _onPeerConnected (peerId: PeerId, conn: Connection) {
184
- const idB58Str = peerId.toString()
185
- this.log('connected', idB58Str)
186
+ this.log('connected %p', peerId)
186
187
 
187
188
  try {
188
189
  const { stream, protocol } = await conn.newStream(this.multicodecs)
@@ -193,7 +194,7 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
193
194
  }
194
195
 
195
196
  // Immediately send my own subscriptions to the newly established conn
196
- this._sendSubscriptions(idB58Str, Array.from(this.subscriptions), true)
197
+ this._sendSubscriptions(peerId, Array.from(this.subscriptions), true)
197
198
  }
198
199
 
199
200
  /**
@@ -209,9 +210,8 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
209
210
  /**
210
211
  * Notifies the router that a peer has been connected
211
212
  */
212
- protected _addPeer (peerId: PeerId, protocol: string) {
213
- const id = peerId.toString()
214
- const existing = this.peers.get(id)
213
+ protected _addPeer (peerId: PeerId, protocol: string): PeerStreams {
214
+ const existing = this.peers.get(peerId)
215
215
 
216
216
  // If peer streams already exists, do nothing
217
217
  if (existing != null) {
@@ -219,14 +219,14 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
219
219
  }
220
220
 
221
221
  // else create a new peer streams
222
- this.log('new peer', id)
222
+ this.log('new peer %p', peerId)
223
223
 
224
- const peerStreams = new PeerStreams({
224
+ const peerStreams: PeerStreams = new PeerStreamsImpl({
225
225
  id: peerId,
226
226
  protocol
227
227
  })
228
228
 
229
- this.peers.set(id, peerStreams)
229
+ this.peers.set(peerId, peerStreams)
230
230
  peerStreams.addEventListener('close', () => this._removePeer(peerId), {
231
231
  once: true
232
232
  })
@@ -239,7 +239,7 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
239
239
  */
240
240
  protected _removePeer (peerId: PeerId) {
241
241
  const id = peerId.toString()
242
- const peerStreams = this.peers.get(id)
242
+ const peerStreams = this.peers.get(peerId)
243
243
  if (peerStreams == null) {
244
244
  return
245
245
  }
@@ -248,8 +248,8 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
248
248
  peerStreams.close()
249
249
 
250
250
  // delete peer streams
251
- this.log('delete peer', id)
252
- this.peers.delete(id)
251
+ this.log('delete peer %p', peerId)
252
+ this.peers.delete(peerId)
253
253
 
254
254
  // remove peer from topics map
255
255
  for (const peers of this.topics.values()) {
@@ -264,20 +264,32 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
264
264
  /**
265
265
  * Responsible for processing each RPC message received by other peers.
266
266
  */
267
- async _processMessages (idB58Str: string, stream: AsyncIterable<Uint8Array|BufferList>, peerStreams: PeerStreams) {
267
+ async _processMessages (peerId: PeerId, stream: AsyncIterable<Uint8Array>, peerStreams: PeerStreams) {
268
268
  try {
269
269
  await pipe(
270
270
  stream,
271
271
  async (source) => {
272
272
  for await (const data of source) {
273
- const rpcBytes = data instanceof Uint8Array ? data : data.slice()
274
- const rpcMsg = this._decodeRpc(rpcBytes)
273
+ const rpcMsg = this._decodeRpc(data)
275
274
 
276
275
  // Since _processRpc may be overridden entirely in unsafe ways,
277
276
  // the simplest/safest option here is to wrap in a function and capture all errors
278
277
  // to prevent a top-level unhandled exception
279
278
  // This processing of rpc messages should happen without awaiting full validation/execution of prior messages
280
- this._processRpc(idB58Str, peerStreams, rpcMsg)
279
+ this.processRpc(peerId, peerStreams, {
280
+ subscriptions: (rpcMsg.subscriptions).map(sub => ({
281
+ subscribe: Boolean(sub.subscribe),
282
+ topicID: sub.topicID ?? ''
283
+ })),
284
+ msgs: (rpcMsg.msgs ?? []).map(msg => ({
285
+ from: msg.from ?? peerId.multihash.bytes,
286
+ data: msg.data ?? new Uint8Array(0),
287
+ topicIDs: msg.topicIDs ?? [],
288
+ seqno: msg.seqno ?? undefined,
289
+ signature: msg.signature ?? undefined,
290
+ key: msg.key ?? undefined
291
+ }))
292
+ })
281
293
  .catch(err => this.log(err))
282
294
  }
283
295
  }
@@ -290,23 +302,23 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
290
302
  /**
291
303
  * Handles an rpc request from a peer
292
304
  */
293
- async _processRpc (idB58Str: string, peerStreams: PeerStreams, rpc: RPC) {
294
- this.log('rpc from', idB58Str)
305
+ async processRpc (from: PeerId, peerStreams: PeerStreams, rpc: RPC) {
306
+ this.log('rpc from %p', from)
295
307
  const subs = rpc.subscriptions
296
308
  const msgs = rpc.msgs
297
309
 
298
310
  if (subs.length > 0) {
299
311
  // update peer subscriptions
300
312
  subs.forEach((subOpt) => {
301
- this._processRpcSubOpt(idB58Str, subOpt)
313
+ this._processRpcSubOpt(from, subOpt)
302
314
  })
303
315
  this.dispatchEvent(new CustomEvent('pubsub:subscription-change', {
304
316
  detail: { peerId: peerStreams.id, subscriptions: subs }
305
317
  }))
306
318
  }
307
319
 
308
- if (!this._acceptFrom(idB58Str)) {
309
- this.log('received message from unacceptable peer %s', idB58Str)
320
+ if (!this._acceptFrom(from)) {
321
+ this.log('received message from unacceptable peer %p', from)
310
322
  return false
311
323
  }
312
324
 
@@ -321,9 +333,12 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
321
333
  }
322
334
 
323
335
  try {
324
- const msg = utils.normalizeInRpcMessage(message, idB58Str)
336
+ const msg = toMessage({
337
+ ...message,
338
+ from: from.multihash.bytes
339
+ })
325
340
 
326
- await this._processRpcMessage(msg)
341
+ await this._processMessage(msg)
327
342
  } catch (err: any) {
328
343
  this.log.error(err)
329
344
  }
@@ -336,7 +351,7 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
336
351
  /**
337
352
  * Handles a subscription change from a peer
338
353
  */
339
- _processRpcSubOpt (id: string, subOpt: RPC.ISubOpts) {
354
+ _processRpcSubOpt (id: PeerId, subOpt: RPCSubscription) {
340
355
  const t = subOpt.topicID
341
356
 
342
357
  if (t == null) {
@@ -349,20 +364,20 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
349
364
  this.topics.set(t, topicSet)
350
365
  }
351
366
 
352
- if (subOpt.subscribe === true) {
367
+ if (subOpt.subscribe) {
353
368
  // subscribe peer to new topic
354
- topicSet.add(id)
369
+ topicSet.add(id.toString())
355
370
  } else {
356
371
  // unsubscribe from existing topic
357
- topicSet.delete(id)
372
+ topicSet.delete(id.toString())
358
373
  }
359
374
  }
360
375
 
361
376
  /**
362
377
  * Handles an message from a peer
363
378
  */
364
- async _processRpcMessage (msg: Message) {
365
- if ((msg.from != null) && this.peerId.equals(msg.from) && !this.emitSelf) {
379
+ async _processMessage (msg: Message) {
380
+ if (this.peerId.equals(msg.from) && !this.emitSelf) {
366
381
  return
367
382
  }
368
383
 
@@ -375,15 +390,15 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
375
390
  }
376
391
 
377
392
  // Emit to self
378
- this._emitMessage(msg)
393
+ this.emitMessage(msg)
379
394
 
380
- return await this._publish(utils.normalizeOutRpcMessage(msg))
395
+ return await this._publish(toRpcMessage(msg))
381
396
  }
382
397
 
383
398
  /**
384
399
  * Emit a message from a peer
385
400
  */
386
- _emitMessage (message: Message) {
401
+ emitMessage (message: Message) {
387
402
  message.topicIDs.forEach((topic) => {
388
403
  if (this.subscriptions.has(topic)) {
389
404
  this.dispatchEvent(new CustomEvent(topic, {
@@ -401,10 +416,13 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
401
416
  const signaturePolicy = this.globalSignaturePolicy
402
417
  switch (signaturePolicy) {
403
418
  case 'StrictSign':
404
- // @ts-expect-error seqno is optional in protobuf definition but it will exist
405
- return utils.msgId(msg.from, msg.seqno)
419
+ if (msg.seqno == null) {
420
+ throw errcode(new Error('Need seqno when signature policy is StrictSign but it was missing'), codes.ERR_MISSING_SEQNO)
421
+ }
422
+
423
+ return msgId(msg.from, msg.seqno)
406
424
  case 'StrictNoSign':
407
- return utils.noSignMsgId(msg.data)
425
+ return noSignMsgId(msg.data)
408
426
  default:
409
427
  throw errcode(new Error('Cannot get message id: unhandled signature policy'), codes.ERR_UNHANDLED_SIGNATURE_POLICY)
410
428
  }
@@ -414,7 +432,7 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
414
432
  * Whether to accept a message from a peer
415
433
  * Override to create a graylist
416
434
  */
417
- _acceptFrom (id: string) {
435
+ _acceptFrom (id: PeerId) {
418
436
  return true
419
437
  }
420
438
 
@@ -423,7 +441,7 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
423
441
  * This can be override to use a custom router protobuf.
424
442
  */
425
443
  _decodeRpc (bytes: Uint8Array) {
426
- return RPC.decode(bytes)
444
+ return RPCProto.decode(bytes)
427
445
  }
428
446
 
429
447
  /**
@@ -431,27 +449,29 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
431
449
  * This can be override to use a custom router protobuf.
432
450
  */
433
451
  _encodeRpc (rpc: IRPC) {
434
- return RPC.encode(rpc).finish()
452
+ return RPCProto.encode(rpc).finish()
435
453
  }
436
454
 
437
455
  /**
438
456
  * Send an rpc object to a peer
439
457
  */
440
- _sendRpc (id: string, rpc: IRPC) {
441
- const peerStreams = this.peers.get(id)
442
- if ((peerStreams == null) || !peerStreams.isWritable) {
443
- const msg = `Cannot send RPC to ${id} as there is no open stream to it available`
458
+ _sendRpc (peer: PeerId, rpc: IRPC) {
459
+ const peerStreams = this.peers.get(peer)
460
+
461
+ if (peerStreams == null || !peerStreams.isWritable) {
462
+ const msg = `Cannot send RPC to ${peer.toString(base58btc)} as there is no open stream to it available`
444
463
 
445
464
  this.log.error(msg)
446
465
  return
447
466
  }
467
+
448
468
  peerStreams.write(this._encodeRpc(rpc))
449
469
  }
450
470
 
451
471
  /**
452
472
  * Send subscriptions to a peer
453
473
  */
454
- _sendSubscriptions (id: string, topics: string[], subscribe: boolean) {
474
+ _sendSubscriptions (id: PeerId, topics: string[], subscribe: boolean) {
455
475
  return this._sendRpc(id, {
456
476
  subscriptions: topics.map(t => ({ topicID: t, subscribe: subscribe }))
457
477
  })
@@ -465,9 +485,6 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
465
485
  const signaturePolicy = this.globalSignaturePolicy
466
486
  switch (signaturePolicy) {
467
487
  case 'StrictNoSign':
468
- if (message.from != null) {
469
- throw errcode(new Error('StrictNoSigning: from should not be present'), codes.ERR_UNEXPECTED_FROM)
470
- }
471
488
  if (message.signature != null) {
472
489
  throw errcode(new Error('StrictNoSigning: signature should not be present'), codes.ERR_UNEXPECTED_SIGNATURE)
473
490
  }
@@ -505,12 +522,11 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
505
522
  * Normalizes the message and signs it, if signing is enabled.
506
523
  * Should be used by the routers to create the message to send.
507
524
  */
508
- protected async _buildMessage (message: Message) {
525
+ protected async _maybeSignMessage (message: Message) {
509
526
  const signaturePolicy = this.globalSignaturePolicy
510
527
  switch (signaturePolicy) {
511
528
  case 'StrictSign':
512
- message.from = this.peerId.multihash.bytes
513
- message.seqno = utils.randomSeqno()
529
+ message.seqno = randomSeqno()
514
530
  return await signMessage(this.peerId, message)
515
531
  case 'StrictNoSign':
516
532
  return await Promise.resolve(message)
@@ -539,7 +555,7 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
539
555
  return []
540
556
  }
541
557
 
542
- return Array.from(peersInTopic)
558
+ return Array.from(peersInTopic).map(str => peerIdFromString(str))
543
559
  }
544
560
 
545
561
  /**
@@ -552,29 +568,27 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
552
568
 
553
569
  this.log('publish', topic, message)
554
570
 
555
- const from = this.peerId.toString()
556
571
  const msgObject = {
557
- receivedFrom: from,
572
+ from: this.peerId,
558
573
  data: message,
559
574
  topicIDs: [topic]
560
575
  }
561
576
 
562
577
  // ensure that the message follows the signature policy
563
- const outMsg = await this._buildMessage(msgObject)
564
- const msg = utils.normalizeInRpcMessage(outMsg)
578
+ const msg = await this._maybeSignMessage(msgObject)
565
579
 
566
580
  // Emit to self if I'm interested and emitSelf enabled
567
- this.emitSelf && this._emitMessage(msg)
581
+ this.emitSelf && this.emitMessage(msg)
568
582
 
569
583
  // send to all the other peers
570
- await this._publish(msg)
584
+ await this._publish(toRpcMessage(msg))
571
585
  }
572
586
 
573
587
  /**
574
588
  * Overriding the implementation of publish should handle the appropriate algorithms for the publish/subscriber implementation.
575
589
  * For example, a Floodsub implementation might simply publish each message to each topic for every peer
576
590
  */
577
- abstract _publish (message: Message): Promise<void>
591
+ abstract _publish (message: RPCMessage): Promise<void>
578
592
 
579
593
  /**
580
594
  * Subscribes to a given topic.
@@ -586,7 +600,10 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
586
600
 
587
601
  if (!this.subscriptions.has(topic)) {
588
602
  this.subscriptions.add(topic)
589
- this.peers.forEach((_, id) => this._sendSubscriptions(id, [topic], true))
603
+
604
+ for (const peerId of this.peers.keys()) {
605
+ this._sendSubscriptions(peerId, [topic], true)
606
+ }
590
607
  }
591
608
  }
592
609
 
@@ -600,7 +617,10 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
600
617
 
601
618
  if (this.subscriptions.has(topic) && this.listenerCount(topic) === 0) {
602
619
  this.subscriptions.delete(topic)
603
- this.peers.forEach((_, id) => this._sendSubscriptions(id, [topic], false))
620
+
621
+ for (const peerId of this.peers.keys()) {
622
+ this._sendSubscriptions(peerId, [topic], false)
623
+ }
604
624
  }
605
625
  }
606
626
 
@@ -614,4 +634,12 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
614
634
 
615
635
  return Array.from(this.subscriptions)
616
636
  }
637
+
638
+ getPeers () {
639
+ if (!this.started) {
640
+ throw new Error('Pubsub is not started')
641
+ }
642
+
643
+ return Array.from(this.peers.keys())
644
+ }
617
645
  }
@@ -2,8 +2,8 @@ import * as PeerIdFactory from '@libp2p/peer-id-factory'
2
2
  import { RPC } from './rpc.js'
3
3
  import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
4
4
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
5
- import { normalizeOutRpcMessage } from '../utils.js'
6
- import { PeerId } from '@libp2p/peer-id'
5
+ import { toRpcMessage } from '../utils.js'
6
+ import type { PeerId } from '@libp2p/interfaces/peer-id'
7
7
  import { keys } from '@libp2p/crypto'
8
8
  import type { Message } from '@libp2p/interfaces/pubsub'
9
9
 
@@ -16,7 +16,7 @@ export async function signMessage (peerId: PeerId, message: Message) {
16
16
  // Get the message in bytes, and prepend with the pubsub prefix
17
17
  const bytes = uint8ArrayConcat([
18
18
  SignPrefix,
19
- RPC.Message.encode(normalizeOutRpcMessage(message)).finish()
19
+ RPC.Message.encode(toRpcMessage(message)).finish()
20
20
  ])
21
21
 
22
22
  if (peerId.privateKey == null) {
@@ -55,7 +55,7 @@ export async function verifySignature (message: Message) {
55
55
  const bytes = uint8ArrayConcat([
56
56
  SignPrefix,
57
57
  RPC.Message.encode({
58
- ...message,
58
+ ...toRpcMessage(message),
59
59
  signature: undefined,
60
60
  key: undefined
61
61
  }).finish()
@@ -79,21 +79,19 @@ export async function messagePublicKey (message: Message) {
79
79
  throw new Error('Could not get the public key from the originator id')
80
80
  }
81
81
 
82
- const from = PeerId.fromBytes(message.from)
83
-
84
82
  if (message.key != null) {
85
83
  const keyPeerId = await PeerIdFactory.createFromPubKey(keys.unmarshalPublicKey(message.key))
86
84
 
87
85
  // the key belongs to the sender, return the key
88
- if (!keyPeerId.equals(from)) {
86
+ if (!keyPeerId.equals(message.from)) {
89
87
  throw new Error('Public Key does not match the originator')
90
88
  }
91
89
 
92
90
  if (keyPeerId.publicKey != null) {
93
91
  return keyPeerId.publicKey
94
92
  }
95
- } else if (from.publicKey != null) {
96
- return from.publicKey
93
+ } else if (message.from.publicKey != null) {
94
+ return message.from.publicKey
97
95
  }
98
96
 
99
97
  // We couldn't validate pubkey is from the originator, error
@@ -25,19 +25,19 @@ export class PeerStreams extends EventEmitter<PeerStreamEvents> {
25
25
  /**
26
26
  * Write stream - it's preferable to use the write method
27
27
  */
28
- public outboundStream: Pushable<Uint8Array> | undefined
28
+ public outboundStream?: Pushable<Uint8Array>
29
29
  /**
30
30
  * Read stream
31
31
  */
32
- public inboundStream: AsyncIterable<Uint8Array> | undefined
32
+ public inboundStream?: AsyncIterable<Uint8Array>
33
33
  /**
34
34
  * The raw outbound stream, as retrieved from conn.newStream
35
35
  */
36
- private _rawOutboundStream: Stream | undefined
36
+ private _rawOutboundStream?: Stream
37
37
  /**
38
38
  * The raw inbound stream, as retrieved from the callback from libp2p.handle
39
39
  */
40
- private _rawInboundStream: Stream | undefined
40
+ private _rawInboundStream?: Stream
41
41
  /**
42
42
  * An AbortController for controlled shutdown of the inbound stream
43
43
  */
@@ -142,6 +142,8 @@ export class PeerStreams extends EventEmitter<PeerStreamEvents> {
142
142
  if (_prevStream == null) {
143
143
  this.dispatchEvent(new CustomEvent('stream:outbound'))
144
144
  }
145
+
146
+ return this.outboundStream
145
147
  }
146
148
 
147
149
  /**