@libp2p/identify 3.0.39-8484de8a2 → 3.0.39-87bc8d4fb

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.
@@ -1,9 +1,10 @@
1
1
  import { serviceCapabilities } from '@libp2p/interface'
2
2
  import { RecordEnvelope, PeerRecord } from '@libp2p/peer-record'
3
- import { debounce, pbStream } from '@libp2p/utils'
4
- import { CODE_P2P } from '@multiformats/multiaddr'
3
+ import { debounce } from '@libp2p/utils/debounce'
4
+ import { protocols } from '@multiformats/multiaddr'
5
5
  import drain from 'it-drain'
6
6
  import parallel from 'it-parallel'
7
+ import { pbStream } from 'it-protobuf-stream'
7
8
  import { setMaxListeners } from 'main-event'
8
9
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
9
10
  import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
@@ -15,7 +16,7 @@ import {
15
16
  import { Identify as IdentifyMessage } from './pb/message.js'
16
17
  import { AbstractIdentify, consumeIdentifyMessage, defaultValues } from './utils.js'
17
18
  import type { IdentifyPush as IdentifyPushInterface, IdentifyPushComponents, IdentifyPushInit } from './index.js'
18
- import type { Stream, Startable, Connection } from '@libp2p/interface'
19
+ import type { Stream, Startable, IncomingStreamData } from '@libp2p/interface'
19
20
  import type { ConnectionManager } from '@libp2p/interface-internal'
20
21
 
21
22
  export class IdentifyPush extends AbstractIdentify implements Startable, IdentifyPushInterface {
@@ -63,21 +64,22 @@ export class IdentifyPush extends AbstractIdentify implements Startable, Identif
63
64
  }
64
65
 
65
66
  try {
66
- const listenAddresses = this.components.addressManager.getAddresses().map(ma => ma.decapsulateCode(CODE_P2P))
67
+ const listenAddresses = this.addressManager.getAddresses().map(ma => ma.decapsulateCode(protocols('p2p').code))
67
68
  const peerRecord = new PeerRecord({
68
- peerId: this.components.peerId,
69
+ peerId: this.peerId,
69
70
  multiaddrs: listenAddresses
70
71
  })
71
- const signedPeerRecord = await RecordEnvelope.seal(peerRecord, this.components.privateKey)
72
- const supportedProtocols = this.components.registrar.getProtocols()
73
- const peer = await this.components.peerStore.get(this.components.peerId)
72
+ const signedPeerRecord = await RecordEnvelope.seal(peerRecord, this.privateKey)
73
+ const supportedProtocols = this.registrar.getProtocols()
74
+ const peer = await this.peerStore.get(this.peerId)
74
75
  const agentVersion = uint8ArrayToString(peer.metadata.get('AgentVersion') ?? uint8ArrayFromString(this.host.agentVersion))
75
76
  const protocolVersion = uint8ArrayToString(peer.metadata.get('ProtocolVersion') ?? uint8ArrayFromString(this.host.protocolVersion))
76
77
  const self = this
77
78
 
78
79
  async function * pushToConnections (): AsyncGenerator<() => Promise<void>> {
79
80
  for (const connection of self.connectionManager.getConnections()) {
80
- const peer = await self.components.peerStore.get(connection.remotePeer)
81
+ const peer = await self.peerStore.get(connection.remotePeer)
82
+ const log = connection.log.newScope('identify-push')
81
83
 
82
84
  if (!peer.protocols.includes(self.protocol)) {
83
85
  continue
@@ -113,9 +115,8 @@ export class IdentifyPush extends AbstractIdentify implements Startable, Identif
113
115
  signal
114
116
  })
115
117
  } catch (err: any) {
116
- // Just log errors if the stream was opened
117
- const log = stream?.log.newScope('identify-push')
118
- log?.error('could not push identify update to peer', err)
118
+ // Just log errors
119
+ log.error('could not push identify update to peer', err)
119
120
  stream?.abort(err)
120
121
  }
121
122
  }
@@ -133,25 +134,32 @@ export class IdentifyPush extends AbstractIdentify implements Startable, Identif
133
134
  /**
134
135
  * Reads the Identify Push message from the given `connection`
135
136
  */
136
- async handleProtocol (stream: Stream, connection: Connection): Promise<void> {
137
- const log = stream.log.newScope('identify-push')
137
+ async handleProtocol (data: IncomingStreamData): Promise<void> {
138
+ const { connection, stream } = data
139
+ const log = connection.log.newScope('identify-push')
138
140
 
139
- if (this.components.peerId.equals(connection.remotePeer)) {
140
- throw new Error('received push from ourselves?')
141
- }
141
+ try {
142
+ if (this.peerId.equals(connection.remotePeer)) {
143
+ throw new Error('received push from ourselves?')
144
+ }
142
145
 
143
- const options = {
144
- signal: AbortSignal.timeout(this.timeout)
145
- }
146
+ const options = {
147
+ signal: AbortSignal.timeout(this.timeout)
148
+ }
146
149
 
147
- const pb = pbStream(stream, {
148
- maxDataLength: this.maxMessageSize
149
- }).pb(IdentifyMessage)
150
+ const pb = pbStream(stream, {
151
+ maxDataLength: this.maxMessageSize
152
+ }).pb(IdentifyMessage)
150
153
 
151
- const message = await pb.read(options)
152
- await stream.close(options)
154
+ const message = await pb.read(options)
155
+ await stream.close(options)
153
156
 
154
- await consumeIdentifyMessage(this.components.peerStore, this.components.events, log, connection, message)
157
+ await consumeIdentifyMessage(this.peerStore, this.events, log, connection, message)
158
+ } catch (err: any) {
159
+ log.error('received invalid message', err)
160
+ stream.abort(err)
161
+ return
162
+ }
155
163
 
156
164
  log.trace('handled push from %p', connection.remotePeer)
157
165
  }
package/src/identify.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  import { publicKeyFromProtobuf, publicKeyToProtobuf } from '@libp2p/crypto/keys'
2
- import { InvalidMessageError, serviceCapabilities } from '@libp2p/interface'
2
+ import { InvalidMessageError, UnsupportedProtocolError, serviceCapabilities } from '@libp2p/interface'
3
3
  import { peerIdFromCID } from '@libp2p/peer-id'
4
4
  import { RecordEnvelope, PeerRecord } from '@libp2p/peer-record'
5
- import { isGlobalUnicast, isPrivate, pbStream } from '@libp2p/utils'
6
- import { CODE_IP6, CODE_IP6ZONE, CODE_P2P } from '@multiformats/multiaddr'
5
+ import { isGlobalUnicast } from '@libp2p/utils/multiaddr/is-global-unicast'
6
+ import { isPrivate } from '@libp2p/utils/multiaddr/is-private'
7
+ import { CODE_IP6, CODE_IP6ZONE, protocols } from '@multiformats/multiaddr'
7
8
  import { IP_OR_DOMAIN, TCP } from '@multiformats/multiaddr-matcher'
9
+ import { pbStream } from 'it-protobuf-stream'
8
10
  import { setMaxListeners } from 'main-event'
9
11
  import {
10
12
  MULTICODEC_IDENTIFY_PROTOCOL_NAME,
@@ -13,7 +15,7 @@ import {
13
15
  import { Identify as IdentifyMessage } from './pb/message.js'
14
16
  import { AbstractIdentify, consumeIdentifyMessage, defaultValues, getCleanMultiaddr } from './utils.js'
15
17
  import type { Identify as IdentifyInterface, IdentifyComponents, IdentifyInit } from './index.js'
16
- import type { IdentifyResult, AbortOptions, Connection, Stream, Startable, Logger } from '@libp2p/interface'
18
+ import type { IdentifyResult, AbortOptions, Connection, Stream, Startable, IncomingStreamData, Logger } from '@libp2p/interface'
17
19
 
18
20
  export class Identify extends AbstractIdentify implements Startable, IdentifyInterface {
19
21
  constructor (components: IdentifyComponents, init: IdentifyInit = {}) {
@@ -28,7 +30,14 @@ export class Identify extends AbstractIdentify implements Startable, IdentifyInt
28
30
  components.events.addEventListener('connection:open', (evt) => {
29
31
  const connection = evt.detail
30
32
  this.identify(connection)
31
- .catch(() => {})
33
+ .catch(err => {
34
+ if (err.name === UnsupportedProtocolError.name) {
35
+ // the remote did not support identify, ignore the error
36
+ return
37
+ }
38
+
39
+ this.log.error('error during identify trigged by connection:open', err)
40
+ })
32
41
  })
33
42
  }
34
43
  }
@@ -39,7 +48,6 @@ export class Identify extends AbstractIdentify implements Startable, IdentifyInt
39
48
 
40
49
  async _identify (connection: Connection, options: AbortOptions = {}): Promise<IdentifyMessage> {
41
50
  let stream: Stream | undefined
42
- let log: Logger | undefined
43
51
 
44
52
  if (options.signal == null) {
45
53
  const signal = AbortSignal.timeout(this.timeout)
@@ -56,21 +64,17 @@ export class Identify extends AbstractIdentify implements Startable, IdentifyInt
56
64
  ...options,
57
65
  runOnLimitedConnection: this.runOnLimitedConnection
58
66
  })
59
- log = stream.log.newScope('identify')
60
67
 
61
68
  const pb = pbStream(stream, {
62
69
  maxDataLength: this.maxMessageSize
63
70
  }).pb(IdentifyMessage)
64
71
 
65
- log('read response')
66
72
  const message = await pb.read(options)
67
73
 
68
- log('close write')
69
- await pb.unwrap().unwrap().close(options)
74
+ await stream.close(options)
70
75
 
71
76
  return message
72
77
  } catch (err: any) {
73
- log?.error('identify failed - %e', err)
74
78
  stream?.abort(err)
75
79
  throw err
76
80
  }
@@ -90,41 +94,43 @@ export class Identify extends AbstractIdentify implements Startable, IdentifyInt
90
94
 
91
95
  const key = publicKeyFromProtobuf(publicKey)
92
96
  const id = peerIdFromCID(key.toCID())
97
+ const log = connection.log.newScope('identify')
93
98
 
94
99
  if (!connection.remotePeer.equals(id)) {
95
100
  throw new InvalidMessageError('Identified peer does not match the expected peer')
96
101
  }
97
102
 
98
- if (this.components.peerId.equals(id)) {
103
+ if (this.peerId.equals(id)) {
99
104
  throw new InvalidMessageError('Identified peer is our own peer id?')
100
105
  }
101
106
 
102
107
  // if the observed address is publicly routable, add it to the address
103
108
  // manager for verification via AutoNAT
104
- this.maybeAddObservedAddress(observedAddr)
109
+ this.maybeAddObservedAddress(observedAddr, log)
105
110
 
106
- this.log('completed for peer %p and protocols %o', id, protocols)
111
+ log('completed for peer %p and protocols %o', id, protocols)
107
112
 
108
- return consumeIdentifyMessage(this.components.peerStore, this.components.events, this.log, connection, message)
113
+ return consumeIdentifyMessage(this.peerStore, this.events, log, connection, message)
109
114
  }
110
115
 
111
- private maybeAddObservedAddress (observedAddr: Uint8Array | undefined): void {
116
+ private maybeAddObservedAddress (observedAddr: Uint8Array | undefined, log: Logger): void {
112
117
  const cleanObservedAddr = getCleanMultiaddr(observedAddr)
113
118
 
114
119
  if (cleanObservedAddr == null) {
115
120
  return
116
121
  }
117
122
 
118
- this.log.trace('our observed address was %a', cleanObservedAddr)
123
+ log.trace('our observed address was %a', cleanObservedAddr)
119
124
 
120
125
  if (isPrivate(cleanObservedAddr)) {
126
+ this.log.trace('our observed address was private')
121
127
  return
122
128
  }
123
129
 
124
130
  const tuples = cleanObservedAddr.getComponents()
125
131
 
126
132
  if (((tuples[0].code === CODE_IP6) || (tuples[0].code === CODE_IP6ZONE && tuples[1].code === CODE_IP6)) && !isGlobalUnicast(cleanObservedAddr)) {
127
- this.log.trace('our observed address was IPv6 but not a global unicast address')
133
+ log.trace('our observed address was IPv6 but not a global unicast address')
128
134
  return
129
135
  }
130
136
 
@@ -136,62 +142,63 @@ export class Identify extends AbstractIdentify implements Startable, IdentifyInt
136
142
  return
137
143
  }
138
144
 
139
- this.log.trace('storing the observed address')
140
- this.components.addressManager.addObservedAddr(cleanObservedAddr)
145
+ log.trace('storing the observed address')
146
+ this.addressManager.addObservedAddr(cleanObservedAddr)
141
147
  }
142
148
 
143
149
  /**
144
150
  * Sends the `Identify` response with the Signed Peer Record
145
151
  * to the requesting peer over the given `connection`
146
152
  */
147
- async handleProtocol (stream: Stream, connection: Connection): Promise<void> {
148
- const log = stream.log.newScope('identify')
153
+ async handleProtocol (data: IncomingStreamData): Promise<void> {
154
+ const { connection, stream } = data
155
+ const log = connection.log.newScope('identify')
149
156
 
150
157
  const signal = AbortSignal.timeout(this.timeout)
158
+
151
159
  setMaxListeners(Infinity, signal)
152
160
 
153
- const peerData = await this.components.peerStore.get(this.components.peerId, {
154
- signal
155
- })
156
- const multiaddrs = this.components.addressManager.getAddresses().map(ma => ma.decapsulateCode(CODE_P2P))
157
- let signedPeerRecord = peerData.peerRecordEnvelope
161
+ try {
162
+ const peerData = await this.peerStore.get(this.peerId)
163
+ const multiaddrs = this.addressManager.getAddresses().map(ma => ma.decapsulateCode(protocols('p2p').code))
164
+ let signedPeerRecord = peerData.peerRecordEnvelope
165
+
166
+ if (multiaddrs.length > 0 && signedPeerRecord == null) {
167
+ const peerRecord = new PeerRecord({
168
+ peerId: this.peerId,
169
+ multiaddrs
170
+ })
171
+
172
+ const envelope = await RecordEnvelope.seal(peerRecord, this.privateKey)
173
+ signedPeerRecord = envelope.marshal().subarray()
174
+ }
158
175
 
159
- if (multiaddrs.length > 0 && signedPeerRecord == null) {
160
- const peerRecord = new PeerRecord({
161
- peerId: this.components.peerId,
162
- multiaddrs
163
- })
176
+ let observedAddr: Uint8Array | undefined = connection.remoteAddr.bytes
177
+
178
+ if (!IP_OR_DOMAIN.matches(connection.remoteAddr)) {
179
+ observedAddr = undefined
180
+ }
164
181
 
165
- const envelope = await RecordEnvelope.seal(peerRecord, this.components.privateKey, {
182
+ const pb = pbStream(stream).pb(IdentifyMessage)
183
+
184
+ await pb.write({
185
+ protocolVersion: this.host.protocolVersion,
186
+ agentVersion: this.host.agentVersion,
187
+ publicKey: publicKeyToProtobuf(this.privateKey.publicKey),
188
+ listenAddrs: multiaddrs.map(addr => addr.bytes),
189
+ signedPeerRecord,
190
+ observedAddr,
191
+ protocols: peerData.protocols
192
+ }, {
166
193
  signal
167
194
  })
168
- signedPeerRecord = envelope.marshal().subarray()
169
- }
170
195
 
171
- let observedAddr: Uint8Array | undefined = connection.remoteAddr.bytes
172
-
173
- if (!IP_OR_DOMAIN.matches(connection.remoteAddr)) {
174
- observedAddr = undefined
196
+ await stream.close({
197
+ signal
198
+ })
199
+ } catch (err: any) {
200
+ log.error('could not respond to identify request', err)
201
+ stream.abort(err)
175
202
  }
176
-
177
- const pb = pbStream(stream).pb(IdentifyMessage)
178
-
179
- log('send response')
180
- await pb.write({
181
- protocolVersion: this.host.protocolVersion,
182
- agentVersion: this.host.agentVersion,
183
- publicKey: publicKeyToProtobuf(this.components.privateKey.publicKey),
184
- listenAddrs: multiaddrs.map(addr => addr.bytes),
185
- signedPeerRecord,
186
- observedAddr,
187
- protocols: peerData.protocols
188
- }, {
189
- signal
190
- })
191
-
192
- log('close write')
193
- await pb.unwrap().unwrap().close({
194
- signal
195
- })
196
203
  }
197
204
  }
package/src/utils.ts CHANGED
@@ -7,7 +7,8 @@ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
7
7
  import { IDENTIFY_PROTOCOL_VERSION, MAX_IDENTIFY_MESSAGE_SIZE, MAX_PUSH_CONCURRENCY } from './consts.js'
8
8
  import type { IdentifyComponents, IdentifyInit } from './index.js'
9
9
  import type { Identify as IdentifyMessage } from './pb/message.js'
10
- import type { Libp2pEvents, IdentifyResult, SignedPeerRecord, Logger, Connection, Peer, PeerData, PeerStore, NodeInfo, Startable, Stream } from '@libp2p/interface'
10
+ import type { Libp2pEvents, IdentifyResult, SignedPeerRecord, Logger, Connection, Peer, PeerData, PeerStore, NodeInfo, Startable, PeerId, IncomingStreamData, PrivateKey } from '@libp2p/interface'
11
+ import type { AddressManager, Registrar } from '@libp2p/interface-internal'
11
12
  import type { Multiaddr } from '@multiformats/multiaddr'
12
13
  import type { TypedEventTarget } from 'main-event'
13
14
 
@@ -190,21 +191,31 @@ export abstract class AbstractIdentify implements Startable {
190
191
  agentVersion: string
191
192
  }
192
193
 
193
- protected components: IdentifyComponents
194
194
  protected protocol: string
195
195
  protected started: boolean
196
196
  protected readonly timeout: number
197
+ protected readonly peerId: PeerId
198
+ protected readonly privateKey: PrivateKey
199
+ protected readonly peerStore: PeerStore
200
+ protected readonly registrar: Registrar
201
+ protected readonly addressManager: AddressManager
197
202
  private readonly maxInboundStreams: number
198
203
  private readonly maxOutboundStreams: number
199
204
  protected readonly maxMessageSize: number
200
205
  protected readonly maxObservedAddresses: number
206
+ protected readonly events: TypedEventTarget<Libp2pEvents>
201
207
  protected readonly runOnLimitedConnection: boolean
202
208
  protected readonly log: Logger
203
209
 
204
210
  constructor (components: IdentifyComponents, init: AbstractIdentifyInit) {
205
211
  this.protocol = init.protocol
206
212
  this.started = false
207
- this.components = components
213
+ this.peerId = components.peerId
214
+ this.privateKey = components.privateKey
215
+ this.peerStore = components.peerStore
216
+ this.registrar = components.registrar
217
+ this.addressManager = components.addressManager
218
+ this.events = components.events
208
219
  this.log = init.log
209
220
 
210
221
  this.timeout = init.timeout ?? defaultValues.timeout
@@ -219,8 +230,6 @@ export abstract class AbstractIdentify implements Startable {
219
230
  protocolVersion: `${init.protocolPrefix ?? defaultValues.protocolPrefix}/${IDENTIFY_PROTOCOL_VERSION}`,
220
231
  agentVersion: getAgentVersion(components.nodeInfo, init.agentVersion)
221
232
  }
222
-
223
- this.handleProtocol = this.handleProtocol.bind(this)
224
233
  }
225
234
 
226
235
  isStarted (): boolean {
@@ -232,14 +241,18 @@ export abstract class AbstractIdentify implements Startable {
232
241
  return
233
242
  }
234
243
 
235
- await this.components.peerStore.merge(this.components.peerId, {
244
+ await this.peerStore.merge(this.peerId, {
236
245
  metadata: {
237
246
  AgentVersion: uint8ArrayFromString(this.host.agentVersion),
238
247
  ProtocolVersion: uint8ArrayFromString(this.host.protocolVersion)
239
248
  }
240
249
  })
241
250
 
242
- await this.components.registrar.handle(this.protocol, this.handleProtocol, {
251
+ await this.registrar.handle(this.protocol, (data) => {
252
+ void this.handleProtocol(data).catch(err => {
253
+ this.log.error(err)
254
+ })
255
+ }, {
243
256
  maxInboundStreams: this.maxInboundStreams,
244
257
  maxOutboundStreams: this.maxOutboundStreams,
245
258
  runOnLimitedConnection: this.runOnLimitedConnection
@@ -249,10 +262,10 @@ export abstract class AbstractIdentify implements Startable {
249
262
  }
250
263
 
251
264
  async stop (): Promise<void> {
252
- await this.components.registrar.unhandle(this.protocol)
265
+ await this.registrar.unhandle(this.protocol)
253
266
 
254
267
  this.started = false
255
268
  }
256
269
 
257
- protected abstract handleProtocol (stream: Stream, connection: Connection): Promise<void>
270
+ protected abstract handleProtocol (data: IncomingStreamData): Promise<void>
258
271
  }