@libp2p/pubsub 1.2.9 → 1.2.10

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
@@ -10,32 +10,28 @@ import { toMessage, ensureArray, randomSeqno, noSignMsgId, msgId, toRpcMessage }
10
10
  import {
11
11
  signMessage,
12
12
  verifySignature
13
- } from './message/sign.js'
13
+ } from './sign.js'
14
14
  import type { PeerId } from '@libp2p/interfaces/peer-id'
15
- import type { Registrar, IncomingStreamData } from '@libp2p/interfaces/registrar'
15
+ import type { IncomingStreamData } from '@libp2p/interfaces/registrar'
16
16
  import type { Connection } from '@libp2p/interfaces/connection'
17
- import type { PubSub, Message, StrictNoSign, StrictSign, PubSubOptions, PubSubEvents, RPC, PeerStreams, RPCSubscription, RPCMessage } from '@libp2p/interfaces/pubsub'
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'
17
+ import type { PubSub, Message, StrictNoSign, StrictSign, PubSubInit, PubSubEvents, PeerStreams, PubSubRPCMessage, PubSubRPC, PubSubRPCSubscription, SubscriptionChangeData } from '@libp2p/interfaces/pubsub'
18
+ import { PeerMap, PeerSet } from '@libp2p/peer-collections'
19
+ import { Components, Initializable } from '@libp2p/interfaces/components'
20
+
21
+ const log = logger('libp2p:pubsub')
25
22
 
26
23
  export interface TopicValidator { (topic: string, message: Message): Promise<void> }
27
24
 
28
25
  /**
29
- * PubsubBaseProtocol handles the peers and connections logic for pubsub routers
26
+ * PubSubBaseProtocol handles the peers and connections logic for pubsub routers
30
27
  * and specifies the API that pubsub routers should have.
31
28
  */
32
- export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubEvents> extends EventEmitter<EventMap & PubSubEvents> implements PubSub<EventMap & PubSubEvents> {
33
- public peerId: PeerId
29
+ export abstract class PubSubBaseProtocol<EventMap extends PubSubEvents = PubSubEvents> extends EventEmitter<EventMap & PubSubEvents> implements PubSub<EventMap & PubSubEvents>, Initializable {
34
30
  public started: boolean
35
31
  /**
36
32
  * Map of topics to which peers are subscribed to
37
33
  */
38
- public topics: Map<string, Set<string>>
34
+ public topics: Map<string, PeerSet>
39
35
  /**
40
36
  * List of our subscriptions
41
37
  */
@@ -64,36 +60,29 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
64
60
  */
65
61
  public topicValidators: Map<string, TopicValidator>
66
62
  public queue: Queue
67
- public registrar: Registrar
68
63
  public multicodecs: string[]
64
+ public components: Components = new Components()
69
65
 
70
- protected log: Logger
71
- protected _libp2p: any
72
- private _registrarHandlerId: string | undefined
73
66
  private _registrarTopologyId: string | undefined
67
+ protected enabled: boolean
74
68
 
75
- constructor (props: PubSubOptions) {
69
+ constructor (props: PubSubInit) {
76
70
  super()
77
71
 
78
72
  const {
79
- debugName = 'libp2p:pubsub',
80
73
  multicodecs = [],
81
- peerId,
82
- registrar,
83
74
  globalSignaturePolicy = 'StrictSign',
84
75
  canRelayMessage = false,
85
76
  emitSelf = false,
86
77
  messageProcessingConcurrency = 10
87
78
  } = props
88
79
 
89
- this.log = logger(debugName)
90
80
  this.multicodecs = ensureArray(multicodecs)
91
- this.registrar = registrar
92
- this.peerId = peerId
81
+ this.enabled = props.enabled !== false
93
82
  this.started = false
94
83
  this.topics = new Map()
95
84
  this.subscriptions = new Set()
96
- this.peers = peerMap<PeerStreams>()
85
+ this.peers = new PeerMap<PeerStreams>()
97
86
  this.globalSignaturePolicy = globalSignaturePolicy === 'StrictNoSign' ? 'StrictNoSign' : 'StrictSign'
98
87
  this.canRelayMessage = canRelayMessage
99
88
  this.emitSelf = emitSelf
@@ -105,6 +94,10 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
105
94
  this._onPeerDisconnected = this._onPeerDisconnected.bind(this)
106
95
  }
107
96
 
97
+ init (components: Components) {
98
+ this.components = components
99
+ }
100
+
108
101
  // LIFECYCLE METHODS
109
102
 
110
103
  /**
@@ -113,15 +106,15 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
113
106
  * @returns {void}
114
107
  */
115
108
  async start () {
116
- if (this.started) {
109
+ if (this.started || !this.enabled) {
117
110
  return
118
111
  }
119
112
 
120
- this.log('starting')
113
+ log('starting')
121
114
 
122
115
  // Incoming streams
123
116
  // Called after a peer dials us
124
- this._registrarHandlerId = await this.registrar.handle(this.multicodecs, this._onIncomingStream)
117
+ await this.components.getRegistrar().handle(this.multicodecs, this._onIncomingStream)
125
118
 
126
119
  // register protocol with topology
127
120
  // Topology callbacks called on connection manager changes
@@ -129,9 +122,9 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
129
122
  onConnect: this._onPeerConnected,
130
123
  onDisconnect: this._onPeerDisconnected
131
124
  })
132
- this._registrarTopologyId = this.registrar.register(this.multicodecs, topology)
125
+ this._registrarTopologyId = await this.components.getRegistrar().register(this.multicodecs, topology)
133
126
 
134
- this.log('started')
127
+ log('started')
135
128
  this.started = true
136
129
  }
137
130
 
@@ -139,19 +132,18 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
139
132
  * Unregister the pubsub protocol and the streams with other peers will be closed.
140
133
  */
141
134
  async stop () {
142
- if (!this.started) {
135
+ if (!this.started || !this.enabled) {
143
136
  return
144
137
  }
145
138
 
146
139
  // unregister protocol and handlers
147
140
  if (this._registrarTopologyId != null) {
148
- this.registrar.unregister(this._registrarTopologyId)
149
- }
150
- if (this._registrarHandlerId != null) {
151
- await this.registrar.unhandle(this._registrarHandlerId)
141
+ this.components.getRegistrar().unregister(this._registrarTopologyId)
152
142
  }
153
143
 
154
- this.log('stopping')
144
+ await this.components.getRegistrar().unhandle(this.multicodecs)
145
+
146
+ log('stopping')
155
147
  for (const peerStreams of this.peers.values()) {
156
148
  peerStreams.close()
157
149
  }
@@ -159,7 +151,7 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
159
151
  this.peers.clear()
160
152
  this.subscriptions = new Set()
161
153
  this.started = false
162
- this.log('stopped')
154
+ log('stopped')
163
155
  }
164
156
 
165
157
  isStarted () {
@@ -169,28 +161,28 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
169
161
  /**
170
162
  * On an inbound stream opened
171
163
  */
172
- protected _onIncomingStream (evt: CustomEvent<IncomingStreamData>) {
173
- const { protocol, stream, connection } = evt.detail
164
+ protected _onIncomingStream (data: IncomingStreamData) {
165
+ const { protocol, stream, connection } = data
174
166
  const peerId = connection.remotePeer
175
167
  const peer = this.addPeer(peerId, protocol)
176
168
  const inboundStream = peer.attachInboundStream(stream)
177
169
 
178
170
  this.processMessages(peerId, inboundStream, peer)
179
- .catch(err => this.log(err))
171
+ .catch(err => log(err))
180
172
  }
181
173
 
182
174
  /**
183
175
  * Registrar notifies an established connection with pubsub protocol
184
176
  */
185
177
  protected async _onPeerConnected (peerId: PeerId, conn: Connection) {
186
- this.log('connected %p', peerId)
178
+ log('connected %p', peerId)
187
179
 
188
180
  try {
189
181
  const { stream, protocol } = await conn.newStream(this.multicodecs)
190
182
  const peer = this.addPeer(peerId, protocol)
191
183
  await peer.attachOutboundStream(stream)
192
184
  } catch (err: any) {
193
- this.log.error(err)
185
+ log.error(err)
194
186
  }
195
187
 
196
188
  // Immediately send my own subscriptions to the newly established conn
@@ -203,7 +195,7 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
203
195
  protected _onPeerDisconnected (peerId: PeerId, conn?: Connection) {
204
196
  const idB58Str = peerId.toString()
205
197
 
206
- this.log('connection ended', idB58Str)
198
+ log('connection ended', idB58Str)
207
199
  this._removePeer(peerId)
208
200
  }
209
201
 
@@ -219,7 +211,7 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
219
211
  }
220
212
 
221
213
  // else create a new peer streams
222
- this.log('new peer %p', peerId)
214
+ log('new peer %p', peerId)
223
215
 
224
216
  const peerStreams: PeerStreams = new PeerStreamsImpl({
225
217
  id: peerId,
@@ -238,7 +230,6 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
238
230
  * Notifies the router that a peer has been disconnected
239
231
  */
240
232
  protected _removePeer (peerId: PeerId) {
241
- const id = peerId.toString()
242
233
  const peerStreams = this.peers.get(peerId)
243
234
  if (peerStreams == null) {
244
235
  return
@@ -248,12 +239,12 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
248
239
  peerStreams.close()
249
240
 
250
241
  // delete peer streams
251
- this.log('delete peer %p', peerId)
242
+ log('delete peer %p', peerId)
252
243
  this.peers.delete(peerId)
253
244
 
254
245
  // remove peer from topics map
255
246
  for (const peers of this.topics.values()) {
256
- peers.delete(id)
247
+ peers.delete(peerId)
257
248
  }
258
249
 
259
250
  return peerStreams
@@ -271,11 +262,11 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
271
262
  async (source) => {
272
263
  for await (const data of source) {
273
264
  const rpcMsg = this.decodeRpc(data)
274
- const messages: RPCMessage[] = []
265
+ const messages: PubSubRPCMessage[] = []
275
266
 
276
267
  for (const msg of (rpcMsg.messages ?? [])) {
277
268
  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)
269
+ log('message from %p was missing from, data or topic fields, dropping', peerId)
279
270
  continue
280
271
  }
281
272
 
@@ -283,7 +274,7 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
283
274
  from: msg.from,
284
275
  data: msg.data,
285
276
  topic: msg.topic,
286
- seqno: msg.seqno ?? undefined,
277
+ sequenceNumber: msg.sequenceNumber ?? undefined,
287
278
  signature: msg.signature ?? undefined,
288
279
  key: msg.key ?? undefined
289
280
  })
@@ -294,13 +285,13 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
294
285
  // to prevent a top-level unhandled exception
295
286
  // This processing of rpc messages should happen without awaiting full validation/execution of prior messages
296
287
  this.processRpc(peerId, peerStreams, {
297
- subscriptions: (rpcMsg.subscriptions).map(sub => ({
288
+ subscriptions: (rpcMsg.subscriptions ?? []).map(sub => ({
298
289
  subscribe: Boolean(sub.subscribe),
299
290
  topic: sub.topic ?? ''
300
291
  })),
301
292
  messages
302
293
  })
303
- .catch(err => this.log(err))
294
+ .catch(err => log(err))
304
295
  }
305
296
  }
306
297
  )
@@ -312,36 +303,42 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
312
303
  /**
313
304
  * Handles an rpc request from a peer
314
305
  */
315
- async processRpc (from: PeerId, peerStreams: PeerStreams, rpc: RPC) {
306
+ async processRpc (from: PeerId, peerStreams: PeerStreams, rpc: PubSubRPC): Promise<boolean> {
316
307
  if (!this.acceptFrom(from)) {
317
- this.log('received message from unacceptable peer %p', from)
318
- return
308
+ log('received message from unacceptable peer %p', from)
309
+ return false
319
310
  }
320
311
 
321
- this.log('rpc from %p', from)
312
+ log('rpc from %p', from)
322
313
 
323
314
  const { subscriptions, messages } = rpc
324
315
 
325
- if (subscriptions.length > 0) {
326
- this.log('subscription update from %p', from)
316
+ if (subscriptions != null && subscriptions.length > 0) {
317
+ log('subscription update from %p', from)
327
318
 
328
319
  // update peer subscriptions
329
320
  subscriptions.forEach((subOpt) => {
330
321
  this.processRpcSubOpt(from, subOpt)
331
322
  })
332
323
 
333
- super.dispatchEvent(new CustomEvent('pubsub:subscription-change', {
334
- detail: { peerId: peerStreams.id, subscriptions }
324
+ super.dispatchEvent(new CustomEvent<SubscriptionChangeData>('pubsub:subscription-change', {
325
+ detail: {
326
+ peerId: peerStreams.id,
327
+ subscriptions: subscriptions.map(({ topic, subscribe }) => ({
328
+ topic: `${topic ?? ''}`,
329
+ subscribe: Boolean(subscribe)
330
+ }))
331
+ }
335
332
  }))
336
333
  }
337
334
 
338
- if (messages.length > 0) {
339
- this.log('messages from %p', from)
335
+ if (messages != null && messages.length > 0) {
336
+ log('messages from %p', from)
340
337
 
341
338
  this.queue.addAll(messages.map(message => async () => {
342
- if (!this.subscriptions.has(message.topic) && !this.canRelayMessage) {
343
- this.log('received message we didn\'t subscribe to. Dropping.')
344
- return
339
+ if (message.topic == null || (!this.subscriptions.has(message.topic) && !this.canRelayMessage)) {
340
+ log('received message we didn\'t subscribe to. Dropping.')
341
+ return false
345
342
  }
346
343
 
347
344
  try {
@@ -349,17 +346,19 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
349
346
 
350
347
  await this.processMessage(from, msg)
351
348
  } catch (err: any) {
352
- this.log.error(err)
349
+ log.error(err)
353
350
  }
354
351
  }))
355
- .catch(err => this.log(err))
352
+ .catch(err => log(err))
356
353
  }
354
+
355
+ return true
357
356
  }
358
357
 
359
358
  /**
360
359
  * Handles a subscription change from a peer
361
360
  */
362
- processRpcSubOpt (id: PeerId, subOpt: RPCSubscription) {
361
+ processRpcSubOpt (id: PeerId, subOpt: PubSubRPCSubscription) {
363
362
  const t = subOpt.topic
364
363
 
365
364
  if (t == null) {
@@ -368,24 +367,24 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
368
367
 
369
368
  let topicSet = this.topics.get(t)
370
369
  if (topicSet == null) {
371
- topicSet = new Set()
370
+ topicSet = new PeerSet()
372
371
  this.topics.set(t, topicSet)
373
372
  }
374
373
 
375
- if (subOpt.subscribe) {
374
+ if (subOpt.subscribe === true) {
376
375
  // subscribe peer to new topic
377
- topicSet.add(id.toString())
376
+ topicSet.add(id)
378
377
  } else {
379
378
  // unsubscribe from existing topic
380
- topicSet.delete(id.toString())
379
+ topicSet.delete(id)
381
380
  }
382
381
  }
383
382
 
384
383
  /**
385
- * Handles an message from a peer
384
+ * Handles a message from a peer
386
385
  */
387
386
  async processMessage (from: PeerId, msg: Message) {
388
- if (this.peerId.equals(from) && !this.emitSelf) {
387
+ if (this.components.getPeerId().equals(from) && !this.emitSelf) {
389
388
  return
390
389
  }
391
390
 
@@ -393,15 +392,15 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
393
392
  try {
394
393
  await this.validate(msg)
395
394
  } catch (err: any) {
396
- this.log('Message is invalid, dropping it. %O', err)
395
+ log('Message is invalid, dropping it. %O', err)
397
396
  return
398
397
  }
399
398
 
400
399
  if (this.subscriptions.has(msg.topic)) {
401
- const isFromSelf = this.peerId.equals(from)
400
+ const isFromSelf = this.components.getPeerId().equals(from)
402
401
 
403
402
  if (!isFromSelf || this.emitSelf) {
404
- super.dispatchEvent(new CustomEvent(msg.topic, {
403
+ super.dispatchEvent(new CustomEvent<Message>(msg.topic, {
405
404
  detail: msg
406
405
  }))
407
406
  }
@@ -418,7 +417,7 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
418
417
  const signaturePolicy = this.globalSignaturePolicy
419
418
  switch (signaturePolicy) {
420
419
  case 'StrictSign':
421
- if (msg.seqno == null) {
420
+ if (msg.sequenceNumber == null) {
422
421
  throw errcode(new Error('Need seqno when signature policy is StrictSign but it was missing'), codes.ERR_MISSING_SEQNO)
423
422
  }
424
423
 
@@ -426,7 +425,7 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
426
425
  throw errcode(new Error('Need key when signature policy is StrictSign but it was missing'), codes.ERR_MISSING_KEY)
427
426
  }
428
427
 
429
- return msgId(msg.key, msg.seqno)
428
+ return msgId(msg.key, msg.sequenceNumber)
430
429
  case 'StrictNoSign':
431
430
  return noSignMsgId(msg.data)
432
431
  default:
@@ -446,17 +445,25 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
446
445
  * Decode Uint8Array into an RPC object.
447
446
  * This can be override to use a custom router protobuf.
448
447
  */
449
- decodeRpc (bytes: Uint8Array) {
450
- return RPCProto.decode(bytes)
451
- }
448
+ abstract decodeRpc (bytes: Uint8Array): PubSubRPC
452
449
 
453
450
  /**
454
451
  * Encode RPC object into a Uint8Array.
455
452
  * This can be override to use a custom router protobuf.
456
453
  */
457
- encodeRpc (rpc: IRPC) {
458
- return RPCProto.encode(rpc).finish()
459
- }
454
+ abstract encodeRpc (rpc: PubSubRPC): Uint8Array
455
+
456
+ /**
457
+ * Decode Uint8Array into an RPC object.
458
+ * This can be override to use a custom router protobuf.
459
+ */
460
+ abstract decodeMessage (bytes: Uint8Array): PubSubRPCMessage
461
+
462
+ /**
463
+ * Encode RPC object into a Uint8Array.
464
+ * This can be override to use a custom router protobuf.
465
+ */
466
+ abstract encodeMessage (rpc: PubSubRPCMessage): Uint8Array
460
467
 
461
468
  /**
462
469
  * Send an rpc object to a peer
@@ -473,13 +480,12 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
473
480
  /**
474
481
  * Send an rpc object to a peer
475
482
  */
476
- sendRpc (peer: PeerId, rpc: IRPC) {
483
+ sendRpc (peer: PeerId, rpc: PubSubRPC) {
477
484
  const peerStreams = this.peers.get(peer)
478
485
 
479
486
  if (peerStreams == null || !peerStreams.isWritable) {
480
- const msg = `Cannot send RPC to ${peer.toString(base58btc)} as there is no open stream to it available`
487
+ log.error('Cannot send RPC to %p as there is no open stream to it available', peer)
481
488
 
482
- this.log.error(msg)
483
489
  return
484
490
  }
485
491
 
@@ -500,7 +506,7 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
500
506
  if (message.key != null) {
501
507
  throw errcode(new Error('StrictNoSigning: key should not be present'), codes.ERR_UNEXPECTED_KEY)
502
508
  }
503
- if (message.seqno != null) {
509
+ if (message.sequenceNumber != null) {
504
510
  throw errcode(new Error('StrictNoSigning: seqno should not be present'), codes.ERR_UNEXPECTED_SEQNO)
505
511
  }
506
512
  break
@@ -508,10 +514,10 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
508
514
  if (message.signature == null) {
509
515
  throw errcode(new Error('StrictSigning: Signing required and no signature was present'), codes.ERR_MISSING_SIGNATURE)
510
516
  }
511
- if (message.seqno == null) {
517
+ if (message.sequenceNumber == null) {
512
518
  throw errcode(new Error('StrictSigning: Signing required and no seqno was present'), codes.ERR_MISSING_SEQNO)
513
519
  }
514
- if (!(await verifySignature(message))) {
520
+ if (!(await verifySignature(message, this.encodeMessage.bind(this)))) {
515
521
  throw errcode(new Error('StrictSigning: Invalid message signature'), codes.ERR_INVALID_SIGNATURE)
516
522
  }
517
523
  break
@@ -534,8 +540,8 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
534
540
  const signaturePolicy = this.globalSignaturePolicy
535
541
  switch (signaturePolicy) {
536
542
  case 'StrictSign':
537
- message.seqno = randomSeqno()
538
- return await signMessage(this.peerId, message)
543
+ message.sequenceNumber = randomSeqno()
544
+ return await signMessage(this.components.getPeerId(), message, this.encodeMessage.bind(this))
539
545
  case 'StrictNoSign':
540
546
  return await Promise.resolve(message)
541
547
  default:
@@ -563,29 +569,31 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
563
569
  return []
564
570
  }
565
571
 
566
- return Array.from(peersInTopic).map(str => peerIdFromString(str))
572
+ return Array.from(peersInTopic.values())
567
573
  }
568
574
 
569
575
  /**
570
576
  * Publishes messages to all subscribed peers
571
577
  */
572
- dispatchEvent (event: CustomEvent): boolean {
578
+ dispatchEvent (event: CustomEvent<Uint8Array | Message>): boolean {
573
579
  if (!this.started) {
574
580
  throw new Error('Pubsub has not started')
575
581
  }
576
582
 
577
583
  const topic = event.type
578
- let message: Message = event.detail
584
+ let message: Message
579
585
 
580
- if (message instanceof Uint8Array) {
586
+ if (event.detail instanceof Uint8Array) {
581
587
  message = {
582
- from: this.peerId,
588
+ from: this.components.getPeerId(),
583
589
  topic,
584
- data: message
590
+ data: event.detail
585
591
  }
592
+ } else {
593
+ message = event.detail
586
594
  }
587
595
 
588
- this.log('publish', topic, message)
596
+ log('publish topic: %s from: %p data: %m', topic, message.from, message.data)
589
597
 
590
598
  Promise.resolve().then(async () => {
591
599
  message = await this.buildMessage(message)
@@ -593,7 +601,7 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
593
601
  // dispatch the event if we are interested
594
602
  if (this.emitSelf) {
595
603
  if (this.subscriptions.has(topic)) {
596
- super.dispatchEvent(new CustomEvent(topic, {
604
+ super.dispatchEvent(new CustomEvent<Message>(topic, {
597
605
  detail: message
598
606
  }))
599
607
 
@@ -604,10 +612,10 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
604
612
  }
605
613
 
606
614
  // send to all the other peers
607
- await this.publishMessage(this.peerId, message)
615
+ await this.publishMessage(this.components.getPeerId(), message)
608
616
  })
609
617
  .catch(err => {
610
- this.log.error(err)
618
+ log.error(err)
611
619
  })
612
620
 
613
621
  return true
@@ -615,9 +623,12 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
615
623
 
616
624
  /**
617
625
  * Overriding the implementation of publish should handle the appropriate algorithms for the publish/subscriber implementation.
618
- * For example, a Floodsub implementation might simply publish each message to each topic for every peer
626
+ * For example, a Floodsub implementation might simply publish each message to each topic for every peer.
627
+ *
628
+ * `sender` might be this peer, or we might be forwarding a message on behalf of another peer, in which case sender
629
+ * is the peer we received the message from, which may not be the peer the message was created by.
619
630
  */
620
- abstract publishMessage (peerId: PeerId, message: Message): Promise<void>
631
+ abstract publishMessage (sender: PeerId, message: Message): Promise<void>
621
632
 
622
633
  /**
623
634
  * Subscribes to a given topic.
@@ -662,7 +673,7 @@ export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubE
662
673
  const wasSubscribed = this.subscriptions.has(topicStr)
663
674
  const listeners = this.listenerCount(topicStr)
664
675
 
665
- this.log('unsubscribe from %s - am subscribed %s, listeners %d', topic, wasSubscribed, listeners)
676
+ log('unsubscribe from %s - am subscribed %s, listeners %d', topic, wasSubscribed, listeners)
666
677
 
667
678
  if (wasSubscribed && listeners === 0) {
668
679
  this.subscriptions.delete(topicStr)
@@ -1,10 +1,9 @@
1
- import { RPC } from './rpc.js'
2
1
  import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
3
2
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
4
- import { toRpcMessage } from '../utils.js'
3
+ import { toRpcMessage } from './utils.js'
5
4
  import type { PeerId } from '@libp2p/interfaces/peer-id'
6
5
  import { keys } from '@libp2p/crypto'
7
- import type { Message } from '@libp2p/interfaces/pubsub'
6
+ import type { Message, PubSubRPCMessage } from '@libp2p/interfaces/pubsub'
8
7
  import { peerIdFromKeys } from '@libp2p/peer-id'
9
8
 
10
9
  export const SignPrefix = uint8ArrayFromString('libp2p-pubsub:')
@@ -12,11 +11,11 @@ export const SignPrefix = uint8ArrayFromString('libp2p-pubsub:')
12
11
  /**
13
12
  * Signs the provided message with the given `peerId`
14
13
  */
15
- export async function signMessage (peerId: PeerId, message: Message) {
14
+ export async function signMessage (peerId: PeerId, message: Message, encode: (rpc: PubSubRPCMessage) => Uint8Array) {
16
15
  // Get the message in bytes, and prepend with the pubsub prefix
17
16
  const bytes = uint8ArrayConcat([
18
17
  SignPrefix,
19
- RPC.Message.encode(toRpcMessage(message)).finish()
18
+ encode(toRpcMessage(message))
20
19
  ])
21
20
 
22
21
  if (peerId.privateKey == null) {
@@ -42,7 +41,7 @@ export async function signMessage (peerId: PeerId, message: Message) {
42
41
  /**
43
42
  * Verifies the signature of the given message
44
43
  */
45
- export async function verifySignature (message: Message) {
44
+ export async function verifySignature (message: Message, encode: (rpc: PubSubRPCMessage) => Uint8Array) {
46
45
  if (message.signature == null) {
47
46
  throw new Error('Message must contain a signature to be verified')
48
47
  }
@@ -54,11 +53,11 @@ export async function verifySignature (message: Message) {
54
53
  // Get message sans the signature
55
54
  const bytes = uint8ArrayConcat([
56
55
  SignPrefix,
57
- RPC.Message.encode({
56
+ encode({
58
57
  ...toRpcMessage(message),
59
58
  signature: undefined,
60
59
  key: undefined
61
- }).finish()
60
+ })
62
61
  ])
63
62
 
64
63
  // Get the public key
package/src/utils.ts CHANGED
@@ -2,8 +2,7 @@ import { randomBytes } from 'iso-random-stream'
2
2
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
3
3
  import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
4
4
  import { sha256 } from 'multiformats/hashes/sha2'
5
- import type * as RPC from './message/rpc.js'
6
- import type { Message, RPCMessage } from '@libp2p/interfaces/pubsub'
5
+ import type { Message, PubSubRPCMessage } from '@libp2p/interfaces/pubsub'
7
6
  import { peerIdFromBytes } from '@libp2p/peer-id'
8
7
  import { codes } from './errors.js'
9
8
  import errcode from 'err-code'
@@ -67,7 +66,7 @@ export const ensureArray = function <T> (maybeArray: T | T[]) {
67
66
  return maybeArray
68
67
  }
69
68
 
70
- export const toMessage = (message: RPC.RPC.IMessage): Message => {
69
+ export const toMessage = (message: PubSubRPCMessage): Message => {
71
70
  if (message.from == null) {
72
71
  throw errcode(new Error('RPC message was missing from'), codes.ERR_MISSING_FROM)
73
72
  }
@@ -75,18 +74,18 @@ export const toMessage = (message: RPC.RPC.IMessage): Message => {
75
74
  return {
76
75
  from: peerIdFromBytes(message.from),
77
76
  topic: message.topic ?? '',
78
- seqno: message.seqno == null ? undefined : BigInt(`0x${uint8ArrayToString(message.seqno, 'base16')}`),
77
+ sequenceNumber: message.sequenceNumber == null ? undefined : BigInt(`0x${uint8ArrayToString(message.sequenceNumber, 'base16')}`),
79
78
  data: message.data ?? new Uint8Array(0),
80
79
  signature: message.signature ?? undefined,
81
80
  key: message.key ?? undefined
82
81
  }
83
82
  }
84
83
 
85
- export const toRpcMessage = (message: Message): RPCMessage => {
84
+ export const toRpcMessage = (message: Message): PubSubRPCMessage => {
86
85
  return {
87
86
  from: message.from.multihash.bytes,
88
87
  data: message.data,
89
- seqno: message.seqno == null ? undefined : uint8ArrayFromString(message.seqno.toString(16).padStart(16, '0'), 'base16'),
88
+ sequenceNumber: message.sequenceNumber == null ? undefined : uint8ArrayFromString(message.sequenceNumber.toString(16).padStart(16, '0'), 'base16'),
90
89
  topic: message.topic,
91
90
  signature: message.signature,
92
91
  key: message.key