libp2p 0.44.0 → 0.45.0

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.
Files changed (188) hide show
  1. package/dist/index.min.js +30 -30
  2. package/dist/src/address-manager/index.d.ts +8 -4
  3. package/dist/src/address-manager/index.d.ts.map +1 -1
  4. package/dist/src/address-manager/index.js +37 -6
  5. package/dist/src/address-manager/index.js.map +1 -1
  6. package/dist/src/address-manager/utils.d.ts +2 -0
  7. package/dist/src/address-manager/utils.d.ts.map +1 -0
  8. package/dist/src/address-manager/utils.js +12 -0
  9. package/dist/src/address-manager/utils.js.map +1 -0
  10. package/dist/src/autonat/constants.d.ts +16 -2
  11. package/dist/src/autonat/constants.d.ts.map +1 -1
  12. package/dist/src/autonat/constants.js +16 -2
  13. package/dist/src/autonat/constants.js.map +1 -1
  14. package/dist/src/autonat/index.d.ts +12 -33
  15. package/dist/src/autonat/index.d.ts.map +1 -1
  16. package/dist/src/autonat/index.js +36 -30
  17. package/dist/src/autonat/index.js.map +1 -1
  18. package/dist/src/circuit-relay/server/advert-service.d.ts.map +1 -1
  19. package/dist/src/circuit-relay/server/advert-service.js +2 -2
  20. package/dist/src/circuit-relay/server/advert-service.js.map +1 -1
  21. package/dist/src/circuit-relay/server/index.d.ts.map +1 -1
  22. package/dist/src/circuit-relay/server/index.js +10 -3
  23. package/dist/src/circuit-relay/server/index.js.map +1 -1
  24. package/dist/src/circuit-relay/transport/discovery.d.ts.map +1 -1
  25. package/dist/src/circuit-relay/transport/discovery.js +4 -2
  26. package/dist/src/circuit-relay/transport/discovery.js.map +1 -1
  27. package/dist/src/circuit-relay/transport/index.d.ts +3 -0
  28. package/dist/src/circuit-relay/transport/index.d.ts.map +1 -1
  29. package/dist/src/circuit-relay/transport/index.js +6 -4
  30. package/dist/src/circuit-relay/transport/index.js.map +1 -1
  31. package/dist/src/circuit-relay/transport/listener.d.ts.map +1 -1
  32. package/dist/src/circuit-relay/transport/listener.js +30 -26
  33. package/dist/src/circuit-relay/transport/listener.js.map +1 -1
  34. package/dist/src/circuit-relay/transport/reservation-store.d.ts +5 -4
  35. package/dist/src/circuit-relay/transport/reservation-store.d.ts.map +1 -1
  36. package/dist/src/circuit-relay/transport/reservation-store.js +37 -30
  37. package/dist/src/circuit-relay/transport/reservation-store.js.map +1 -1
  38. package/dist/src/components.d.ts +6 -63
  39. package/dist/src/components.d.ts.map +1 -1
  40. package/dist/src/components.js +60 -164
  41. package/dist/src/components.js.map +1 -1
  42. package/dist/src/config/connection-gater.browser.d.ts +8 -0
  43. package/dist/src/config/connection-gater.browser.d.ts.map +1 -0
  44. package/dist/src/config/connection-gater.browser.js +27 -0
  45. package/dist/src/config/connection-gater.browser.js.map +1 -0
  46. package/dist/src/config/connection-gater.d.ts +6 -0
  47. package/dist/src/config/connection-gater.d.ts.map +1 -0
  48. package/dist/src/config/connection-gater.js +18 -0
  49. package/dist/src/config/connection-gater.js.map +1 -0
  50. package/dist/src/config.d.ts +2 -1
  51. package/dist/src/config.d.ts.map +1 -1
  52. package/dist/src/config.js +0 -62
  53. package/dist/src/config.js.map +1 -1
  54. package/dist/src/connection-manager/auto-dial.d.ts +3 -0
  55. package/dist/src/connection-manager/auto-dial.d.ts.map +1 -1
  56. package/dist/src/connection-manager/auto-dial.js +36 -13
  57. package/dist/src/connection-manager/auto-dial.js.map +1 -1
  58. package/dist/src/connection-manager/connection-pruner.d.ts +4 -0
  59. package/dist/src/connection-manager/connection-pruner.d.ts.map +1 -1
  60. package/dist/src/connection-manager/connection-pruner.js +23 -8
  61. package/dist/src/connection-manager/connection-pruner.js.map +1 -1
  62. package/dist/src/connection-manager/dial-queue.d.ts +2 -1
  63. package/dist/src/connection-manager/dial-queue.d.ts.map +1 -1
  64. package/dist/src/connection-manager/dial-queue.js +36 -14
  65. package/dist/src/connection-manager/dial-queue.js.map +1 -1
  66. package/dist/src/connection-manager/index.d.ts +8 -7
  67. package/dist/src/connection-manager/index.d.ts.map +1 -1
  68. package/dist/src/connection-manager/index.js +23 -34
  69. package/dist/src/connection-manager/index.js.map +1 -1
  70. package/dist/src/content-routing/index.d.ts +0 -10
  71. package/dist/src/content-routing/index.d.ts.map +1 -1
  72. package/dist/src/content-routing/index.js +7 -42
  73. package/dist/src/content-routing/index.js.map +1 -1
  74. package/dist/src/content-routing/utils.d.ts.map +1 -1
  75. package/dist/src/content-routing/utils.js +3 -1
  76. package/dist/src/content-routing/utils.js.map +1 -1
  77. package/dist/src/fetch/index.d.ts +13 -35
  78. package/dist/src/fetch/index.d.ts.map +1 -1
  79. package/dist/src/fetch/index.js +7 -9
  80. package/dist/src/fetch/index.js.map +1 -1
  81. package/dist/src/identify/identify.d.ts +53 -0
  82. package/dist/src/identify/identify.d.ts.map +1 -0
  83. package/dist/src/identify/identify.js +405 -0
  84. package/dist/src/identify/identify.js.map +1 -0
  85. package/dist/src/identify/index.d.ts +12 -58
  86. package/dist/src/identify/index.d.ts.map +1 -1
  87. package/dist/src/identify/index.js +5 -388
  88. package/dist/src/identify/index.js.map +1 -1
  89. package/dist/src/index.d.ts +13 -68
  90. package/dist/src/index.d.ts.map +1 -1
  91. package/dist/src/index.js +2 -1
  92. package/dist/src/index.js.map +1 -1
  93. package/dist/src/insecure/index.js.map +1 -1
  94. package/dist/src/libp2p.d.ts +11 -42
  95. package/dist/src/libp2p.d.ts.map +1 -1
  96. package/dist/src/libp2p.js +137 -196
  97. package/dist/src/libp2p.js.map +1 -1
  98. package/dist/src/peer-routing.d.ts +1 -38
  99. package/dist/src/peer-routing.d.ts.map +1 -1
  100. package/dist/src/peer-routing.js +0 -59
  101. package/dist/src/peer-routing.js.map +1 -1
  102. package/dist/src/ping/constants.d.ts +4 -0
  103. package/dist/src/ping/constants.d.ts.map +1 -1
  104. package/dist/src/ping/constants.js +10 -0
  105. package/dist/src/ping/constants.js.map +1 -1
  106. package/dist/src/ping/index.d.ts +10 -27
  107. package/dist/src/ping/index.d.ts.map +1 -1
  108. package/dist/src/ping/index.js +18 -21
  109. package/dist/src/ping/index.js.map +1 -1
  110. package/dist/src/registrar.d.ts +5 -2
  111. package/dist/src/registrar.d.ts.map +1 -1
  112. package/dist/src/registrar.js +27 -25
  113. package/dist/src/registrar.js.map +1 -1
  114. package/dist/src/transport-manager.d.ts +11 -4
  115. package/dist/src/transport-manager.d.ts.map +1 -1
  116. package/dist/src/transport-manager.js +19 -10
  117. package/dist/src/transport-manager.js.map +1 -1
  118. package/dist/src/upgrader.d.ts +15 -10
  119. package/dist/src/upgrader.d.ts.map +1 -1
  120. package/dist/src/upgrader.js +39 -48
  121. package/dist/src/upgrader.js.map +1 -1
  122. package/dist/src/upnp-nat/index.d.ts +42 -0
  123. package/dist/src/upnp-nat/index.d.ts.map +1 -0
  124. package/dist/src/{nat-manager.js → upnp-nat/index.js} +11 -7
  125. package/dist/src/upnp-nat/index.js.map +1 -0
  126. package/dist/src/version.d.ts +1 -1
  127. package/dist/src/version.d.ts.map +1 -1
  128. package/dist/src/version.js +1 -1
  129. package/dist/src/version.js.map +1 -1
  130. package/dist/typedoc-urls.json +25 -10
  131. package/package.json +61 -45
  132. package/src/address-manager/index.ts +50 -8
  133. package/src/address-manager/utils.ts +14 -0
  134. package/src/autonat/constants.ts +17 -2
  135. package/src/autonat/index.ts +53 -45
  136. package/src/circuit-relay/server/advert-service.ts +2 -2
  137. package/src/circuit-relay/server/index.ts +11 -3
  138. package/src/circuit-relay/transport/discovery.ts +5 -3
  139. package/src/circuit-relay/transport/index.ts +9 -4
  140. package/src/circuit-relay/transport/listener.ts +38 -28
  141. package/src/circuit-relay/transport/reservation-store.ts +34 -20
  142. package/src/components.ts +71 -234
  143. package/src/config/connection-gater.browser.ts +31 -0
  144. package/src/config/connection-gater.ts +19 -0
  145. package/src/config.ts +3 -64
  146. package/src/connection-manager/auto-dial.ts +44 -15
  147. package/src/connection-manager/connection-pruner.ts +28 -8
  148. package/src/connection-manager/dial-queue.ts +41 -21
  149. package/src/connection-manager/index.ts +31 -43
  150. package/src/content-routing/index.ts +7 -55
  151. package/src/content-routing/utils.ts +3 -1
  152. package/src/fetch/index.ts +51 -14
  153. package/src/identify/identify.ts +518 -0
  154. package/src/identify/index.ts +17 -507
  155. package/src/index.ts +14 -77
  156. package/src/insecure/index.ts +4 -4
  157. package/src/libp2p.ts +139 -260
  158. package/src/peer-routing.ts +1 -101
  159. package/src/ping/constants.ts +11 -0
  160. package/src/ping/index.ts +32 -28
  161. package/src/registrar.ts +31 -27
  162. package/src/transport-manager.ts +27 -13
  163. package/src/upgrader.ts +62 -58
  164. package/src/{nat-manager.ts → upnp-nat/index.ts} +16 -17
  165. package/src/version.ts +1 -1
  166. package/dist/src/dht/dht-content-routing.d.ts +0 -17
  167. package/dist/src/dht/dht-content-routing.d.ts.map +0 -1
  168. package/dist/src/dht/dht-content-routing.js +0 -32
  169. package/dist/src/dht/dht-content-routing.js.map +0 -1
  170. package/dist/src/dht/dht-peer-routing.d.ts +0 -15
  171. package/dist/src/dht/dht-peer-routing.d.ts.map +0 -1
  172. package/dist/src/dht/dht-peer-routing.js +0 -26
  173. package/dist/src/dht/dht-peer-routing.js.map +0 -1
  174. package/dist/src/dht/dummy-dht.d.ts +0 -20
  175. package/dist/src/dht/dummy-dht.d.ts.map +0 -1
  176. package/dist/src/dht/dummy-dht.js +0 -46
  177. package/dist/src/dht/dummy-dht.js.map +0 -1
  178. package/dist/src/nat-manager.d.ts +0 -74
  179. package/dist/src/nat-manager.d.ts.map +0 -1
  180. package/dist/src/nat-manager.js.map +0 -1
  181. package/dist/src/peer-record-updater.d.ts +0 -24
  182. package/dist/src/peer-record-updater.d.ts.map +0 -1
  183. package/dist/src/peer-record-updater.js +0 -44
  184. package/dist/src/peer-record-updater.js.map +0 -1
  185. package/src/dht/dht-content-routing.ts +0 -44
  186. package/src/dht/dht-peer-routing.ts +0 -36
  187. package/src/dht/dummy-dht.ts +0 -60
  188. package/src/peer-record-updater.ts +0 -65
@@ -0,0 +1,518 @@
1
+ import { logger } from '@libp2p/logger'
2
+ import { CodeError } from '@libp2p/interfaces/errors'
3
+ import * as lp from 'it-length-prefixed'
4
+ import { pipe } from 'it-pipe'
5
+ import first from 'it-first'
6
+ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
7
+ import { Multiaddr, multiaddr, protocols } from '@multiformats/multiaddr'
8
+ import { Identify } from './pb/message.js'
9
+ import { RecordEnvelope, PeerRecord } from '@libp2p/peer-record'
10
+ import {
11
+ AGENT_VERSION,
12
+ IDENTIFY_PROTOCOL_VERSION,
13
+ MULTICODEC_IDENTIFY_PROTOCOL_NAME,
14
+ MULTICODEC_IDENTIFY_PUSH_PROTOCOL_NAME,
15
+ MULTICODEC_IDENTIFY_PROTOCOL_VERSION,
16
+ MULTICODEC_IDENTIFY_PUSH_PROTOCOL_VERSION
17
+ } from './consts.js'
18
+ import { codes } from '../errors.js'
19
+ import type { IncomingStreamData, Registrar } from '@libp2p/interface-registrar'
20
+ import type { Connection, Stream } from '@libp2p/interface-connection'
21
+ import type { Startable } from '@libp2p/interfaces/startable'
22
+ import { peerIdFromKeys } from '@libp2p/peer-id'
23
+ import type { AbortOptions } from '@libp2p/interfaces'
24
+ import { abortableDuplex } from 'abortable-iterator'
25
+ import { setMaxListeners } from 'events'
26
+ import type { ConnectionManager } from '@libp2p/interface-connection-manager'
27
+ import type { PeerId } from '@libp2p/interface-peer-id'
28
+ import type { Peer, PeerStore } from '@libp2p/interface-peer-store'
29
+ import type { AddressManager } from '@libp2p/interface-address-manager'
30
+ import { anySignal } from 'any-signal'
31
+ import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
32
+ import { pbStream } from 'it-pb-stream'
33
+ import { isNode, isBrowser, isWebWorker, isElectronMain, isElectronRenderer, isReactNative } from 'wherearewe'
34
+ import type { IdentifyServiceComponents, IdentifyServiceInit } from './index.js'
35
+ import type { Libp2pEvents, IdentifyResult, SignedPeerRecord } from '@libp2p/interface-libp2p'
36
+ import type { EventEmitter } from '@libp2p/interfaces/events'
37
+
38
+ const log = logger('libp2p:identify')
39
+
40
+ // https://github.com/libp2p/go-libp2p/blob/8d2e54e1637041d5cf4fac1e531287560bd1f4ac/p2p/protocol/identify/id.go#L52
41
+ const MAX_IDENTIFY_MESSAGE_SIZE = 1024 * 8
42
+
43
+ const defaultValues = {
44
+ protocolPrefix: 'ipfs',
45
+ agentVersion: AGENT_VERSION,
46
+ // https://github.com/libp2p/go-libp2p/blob/8d2e54e1637041d5cf4fac1e531287560bd1f4ac/p2p/protocol/identify/id.go#L48
47
+ timeout: 60000,
48
+ maxInboundStreams: 1,
49
+ maxOutboundStreams: 1,
50
+ maxPushIncomingStreams: 1,
51
+ maxPushOutgoingStreams: 1,
52
+ maxObservedAddresses: 10,
53
+ maxIdentifyMessageSize: 8192
54
+ }
55
+
56
+ export class DefaultIdentifyService implements Startable {
57
+ private readonly identifyProtocolStr: string
58
+ private readonly identifyPushProtocolStr: string
59
+ public readonly host: {
60
+ protocolVersion: string
61
+ agentVersion: string
62
+ }
63
+
64
+ private started: boolean
65
+ private readonly timeout: number
66
+ private readonly peerId: PeerId
67
+ private readonly peerStore: PeerStore
68
+ private readonly registrar: Registrar
69
+ private readonly connectionManager: ConnectionManager
70
+ private readonly addressManager: AddressManager
71
+ private readonly maxInboundStreams: number
72
+ private readonly maxOutboundStreams: number
73
+ private readonly maxPushIncomingStreams: number
74
+ private readonly maxPushOutgoingStreams: number
75
+ private readonly maxIdentifyMessageSize: number
76
+ private readonly maxObservedAddresses: number
77
+ private readonly events: EventEmitter<Libp2pEvents>
78
+
79
+ constructor (components: IdentifyServiceComponents, init: IdentifyServiceInit) {
80
+ this.started = false
81
+ this.peerId = components.peerId
82
+ this.peerStore = components.peerStore
83
+ this.registrar = components.registrar
84
+ this.addressManager = components.addressManager
85
+ this.connectionManager = components.connectionManager
86
+ this.events = components.events
87
+
88
+ this.identifyProtocolStr = `/${init.protocolPrefix ?? defaultValues.protocolPrefix}/${MULTICODEC_IDENTIFY_PROTOCOL_NAME}/${MULTICODEC_IDENTIFY_PROTOCOL_VERSION}`
89
+ this.identifyPushProtocolStr = `/${init.protocolPrefix ?? defaultValues.protocolPrefix}/${MULTICODEC_IDENTIFY_PUSH_PROTOCOL_NAME}/${MULTICODEC_IDENTIFY_PUSH_PROTOCOL_VERSION}`
90
+ this.timeout = init.timeout ?? defaultValues.timeout
91
+ this.maxInboundStreams = init.maxInboundStreams ?? defaultValues.maxInboundStreams
92
+ this.maxOutboundStreams = init.maxOutboundStreams ?? defaultValues.maxOutboundStreams
93
+ this.maxPushIncomingStreams = init.maxPushIncomingStreams ?? defaultValues.maxPushIncomingStreams
94
+ this.maxPushOutgoingStreams = init.maxPushOutgoingStreams ?? defaultValues.maxPushOutgoingStreams
95
+ this.maxIdentifyMessageSize = init.maxIdentifyMessageSize ?? defaultValues.maxIdentifyMessageSize
96
+ this.maxObservedAddresses = init.maxObservedAddresses ?? defaultValues.maxObservedAddresses
97
+
98
+ // Store self host metadata
99
+ this.host = {
100
+ protocolVersion: `${init.protocolPrefix ?? defaultValues.protocolPrefix}/${IDENTIFY_PROTOCOL_VERSION}`,
101
+ agentVersion: init.agentVersion ?? defaultValues.agentVersion
102
+ }
103
+
104
+ // When a new connection happens, trigger identify
105
+ components.events.addEventListener('connection:open', (evt) => {
106
+ const connection = evt.detail
107
+ this.identify(connection).catch(err => { log.error('error during identify trigged by connection:open', err) })
108
+ })
109
+
110
+ // When self peer record changes, trigger identify-push
111
+ components.events.addEventListener('self:peer:update', (evt) => {
112
+ void this.push().catch(err => { log.error(err) })
113
+ })
114
+
115
+ // Append user agent version to default AGENT_VERSION depending on the environment
116
+ if (this.host.agentVersion === AGENT_VERSION) {
117
+ if (isNode || isElectronMain) {
118
+ this.host.agentVersion += ` UserAgent=${globalThis.process.version}`
119
+ } else if (isBrowser || isWebWorker || isElectronRenderer || isReactNative) {
120
+ this.host.agentVersion += ` UserAgent=${globalThis.navigator.userAgent}`
121
+ }
122
+ }
123
+ }
124
+
125
+ isStarted (): boolean {
126
+ return this.started
127
+ }
128
+
129
+ async start (): Promise<void> {
130
+ if (this.started) {
131
+ return
132
+ }
133
+
134
+ await this.peerStore.merge(this.peerId, {
135
+ metadata: {
136
+ AgentVersion: uint8ArrayFromString(this.host.agentVersion),
137
+ ProtocolVersion: uint8ArrayFromString(this.host.protocolVersion)
138
+ }
139
+ })
140
+
141
+ await this.registrar.handle(this.identifyProtocolStr, (data) => {
142
+ void this._handleIdentify(data).catch(err => {
143
+ log.error(err)
144
+ })
145
+ }, {
146
+ maxInboundStreams: this.maxInboundStreams,
147
+ maxOutboundStreams: this.maxOutboundStreams
148
+ })
149
+ await this.registrar.handle(this.identifyPushProtocolStr, (data) => {
150
+ void this._handlePush(data).catch(err => {
151
+ log.error(err)
152
+ })
153
+ }, {
154
+ maxInboundStreams: this.maxPushIncomingStreams,
155
+ maxOutboundStreams: this.maxPushOutgoingStreams
156
+ })
157
+
158
+ this.started = true
159
+ }
160
+
161
+ async stop (): Promise<void> {
162
+ await this.registrar.unhandle(this.identifyProtocolStr)
163
+ await this.registrar.unhandle(this.identifyPushProtocolStr)
164
+
165
+ this.started = false
166
+ }
167
+
168
+ /**
169
+ * Send an Identify Push update to the list of connections
170
+ */
171
+ async pushToConnections (connections: Connection[]): Promise<void> {
172
+ const listenAddresses = this.addressManager.getAddresses().map(ma => ma.decapsulateCode(protocols('p2p').code))
173
+ const peerRecord = new PeerRecord({
174
+ peerId: this.peerId,
175
+ multiaddrs: listenAddresses
176
+ })
177
+ const signedPeerRecord = await RecordEnvelope.seal(peerRecord, this.peerId)
178
+ const supportedProtocols = this.registrar.getProtocols()
179
+ const peer = await this.peerStore.get(this.peerId)
180
+ const agentVersion = uint8ArrayToString(peer.metadata.get('AgentVersion') ?? uint8ArrayFromString(this.host.agentVersion))
181
+ const protocolVersion = uint8ArrayToString(peer.metadata.get('ProtocolVersion') ?? uint8ArrayFromString(this.host.protocolVersion))
182
+
183
+ const pushes = connections.map(async connection => {
184
+ let stream: Stream | undefined
185
+
186
+ const signal = AbortSignal.timeout(this.timeout)
187
+
188
+ try {
189
+ // fails on node < 15.4
190
+ setMaxListeners?.(Infinity, signal)
191
+ } catch {}
192
+
193
+ try {
194
+ stream = await connection.newStream([this.identifyPushProtocolStr], {
195
+ signal
196
+ })
197
+
198
+ // make stream abortable
199
+ const source = abortableDuplex(stream, signal)
200
+
201
+ await source.sink(pipe(
202
+ [Identify.encode({
203
+ listenAddrs: listenAddresses.map(ma => ma.bytes),
204
+ signedPeerRecord: signedPeerRecord.marshal(),
205
+ protocols: supportedProtocols,
206
+ agentVersion,
207
+ protocolVersion
208
+ })],
209
+ (source) => lp.encode(source)
210
+ ))
211
+ } catch (err: any) {
212
+ // Just log errors
213
+ log.error('could not push identify update to peer', err)
214
+ } finally {
215
+ if (stream != null) {
216
+ stream.close()
217
+ }
218
+ }
219
+ })
220
+
221
+ await Promise.all(pushes)
222
+ }
223
+
224
+ /**
225
+ * Calls `push` on all peer connections
226
+ */
227
+ async push (): Promise<void> {
228
+ // Do not try to push if we are not running
229
+ if (!this.isStarted()) {
230
+ return
231
+ }
232
+
233
+ const connections: Connection[] = []
234
+
235
+ await Promise.all(
236
+ this.connectionManager.getConnections().map(async conn => {
237
+ try {
238
+ const peer = await this.peerStore.get(conn.remotePeer)
239
+
240
+ if (!peer.protocols.includes(this.identifyPushProtocolStr)) {
241
+ return
242
+ }
243
+
244
+ connections.push(conn)
245
+ } catch (err: any) {
246
+ if (err.code !== codes.ERR_NOT_FOUND) {
247
+ throw err
248
+ }
249
+ }
250
+ })
251
+ )
252
+
253
+ await this.pushToConnections(connections)
254
+ }
255
+
256
+ async _identify (connection: Connection, options: AbortOptions = {}): Promise<Identify> {
257
+ let stream: Stream | undefined
258
+
259
+ const signal = anySignal([AbortSignal.timeout(this.timeout), options?.signal])
260
+
261
+ try {
262
+ // fails on node < 15.4
263
+ setMaxListeners?.(Infinity, signal)
264
+ } catch {}
265
+
266
+ try {
267
+ stream = await connection.newStream([this.identifyProtocolStr], {
268
+ signal
269
+ })
270
+
271
+ // make stream abortable
272
+ const source = abortableDuplex(stream, signal)
273
+
274
+ const data = await pipe(
275
+ [],
276
+ source,
277
+ (source) => lp.decode(source, {
278
+ maxDataLength: this.maxIdentifyMessageSize ?? MAX_IDENTIFY_MESSAGE_SIZE
279
+ }),
280
+ async (source) => await first(source)
281
+ )
282
+
283
+ if (data == null) {
284
+ throw new CodeError('No data could be retrieved', codes.ERR_CONNECTION_ENDED)
285
+ }
286
+
287
+ try {
288
+ return Identify.decode(data)
289
+ } catch (err: any) {
290
+ throw new CodeError(String(err), codes.ERR_INVALID_MESSAGE)
291
+ }
292
+ } finally {
293
+ if (stream != null) {
294
+ stream.close()
295
+ }
296
+ signal.clear()
297
+ }
298
+ }
299
+
300
+ async identify (connection: Connection, options: AbortOptions = {}): Promise<void> {
301
+ const message = await this._identify(connection, options)
302
+ const {
303
+ publicKey,
304
+ protocols,
305
+ observedAddr
306
+ } = message
307
+
308
+ if (publicKey == null) {
309
+ throw new CodeError('public key was missing from identify message', codes.ERR_MISSING_PUBLIC_KEY)
310
+ }
311
+
312
+ const id = await peerIdFromKeys(publicKey)
313
+
314
+ if (!connection.remotePeer.equals(id)) {
315
+ throw new CodeError('identified peer does not match the expected peer', codes.ERR_INVALID_PEER)
316
+ }
317
+
318
+ if (this.peerId.equals(id)) {
319
+ throw new CodeError('identified peer is our own peer id?', codes.ERR_INVALID_PEER)
320
+ }
321
+
322
+ // Get the observedAddr if there is one
323
+ const cleanObservedAddr = getCleanMultiaddr(observedAddr)
324
+
325
+ log('identify completed for peer %p and protocols %o', id, protocols)
326
+ log('our observed address is %s', cleanObservedAddr)
327
+
328
+ if (cleanObservedAddr != null &&
329
+ this.addressManager.getObservedAddrs().length < (this.maxObservedAddresses ?? Infinity)) {
330
+ log('storing our observed address %s', cleanObservedAddr?.toString())
331
+ this.addressManager.addObservedAddr(cleanObservedAddr)
332
+ }
333
+
334
+ const signedPeerRecord = await this.#consumeIdentifyMessage(connection.remotePeer, message)
335
+
336
+ const result: IdentifyResult = {
337
+ peerId: id,
338
+ protocolVersion: message.protocolVersion,
339
+ agentVersion: message.agentVersion,
340
+ publicKey: message.publicKey,
341
+ listenAddrs: message.listenAddrs.map(buf => multiaddr(buf)),
342
+ observedAddr: message.observedAddr == null ? undefined : multiaddr(message.observedAddr),
343
+ protocols: message.protocols,
344
+ signedPeerRecord
345
+ }
346
+
347
+ this.events.safeDispatchEvent('peer:identify', { detail: result })
348
+ }
349
+
350
+ /**
351
+ * Sends the `Identify` response with the Signed Peer Record
352
+ * to the requesting peer over the given `connection`
353
+ */
354
+ async _handleIdentify (data: IncomingStreamData): Promise<void> {
355
+ const { connection, stream } = data
356
+
357
+ const signal = AbortSignal.timeout(this.timeout)
358
+
359
+ try {
360
+ // fails on node < 15.4
361
+ setMaxListeners?.(Infinity, signal)
362
+ } catch {}
363
+
364
+ try {
365
+ const publicKey = this.peerId.publicKey ?? new Uint8Array(0)
366
+ const peerData = await this.peerStore.get(this.peerId)
367
+ const multiaddrs = this.addressManager.getAddresses().map(ma => ma.decapsulateCode(protocols('p2p').code))
368
+ let signedPeerRecord = peerData.peerRecordEnvelope
369
+
370
+ if (multiaddrs.length > 0 && signedPeerRecord == null) {
371
+ const peerRecord = new PeerRecord({
372
+ peerId: this.peerId,
373
+ multiaddrs
374
+ })
375
+
376
+ const envelope = await RecordEnvelope.seal(peerRecord, this.peerId)
377
+ signedPeerRecord = envelope.marshal().subarray()
378
+ }
379
+
380
+ const message = Identify.encode({
381
+ protocolVersion: this.host.protocolVersion,
382
+ agentVersion: this.host.agentVersion,
383
+ publicKey,
384
+ listenAddrs: multiaddrs.map(addr => addr.bytes),
385
+ signedPeerRecord,
386
+ observedAddr: connection.remoteAddr.bytes,
387
+ protocols: peerData.protocols
388
+ })
389
+
390
+ // make stream abortable
391
+ const source = abortableDuplex(stream, signal)
392
+
393
+ const msgWithLenPrefix = pipe([message], (source) => lp.encode(source))
394
+ await source.sink(msgWithLenPrefix)
395
+ } catch (err: any) {
396
+ log.error('could not respond to identify request', err)
397
+ } finally {
398
+ stream.close()
399
+ }
400
+ }
401
+
402
+ /**
403
+ * Reads the Identify Push message from the given `connection`
404
+ */
405
+ async _handlePush (data: IncomingStreamData): Promise<void> {
406
+ const { connection, stream } = data
407
+
408
+ try {
409
+ if (this.peerId.equals(connection.remotePeer)) {
410
+ throw new Error('received push from ourselves?')
411
+ }
412
+
413
+ // make stream abortable
414
+ const source = abortableDuplex(stream, AbortSignal.timeout(this.timeout))
415
+ const pb = pbStream(source, {
416
+ maxDataLength: this.maxIdentifyMessageSize ?? MAX_IDENTIFY_MESSAGE_SIZE
417
+ })
418
+ const message = await pb.readPB(Identify)
419
+
420
+ await this.#consumeIdentifyMessage(connection.remotePeer, message)
421
+ } catch (err: any) {
422
+ log.error('received invalid message', err)
423
+ return
424
+ } finally {
425
+ stream.close()
426
+ }
427
+
428
+ log('handled push from %p', connection.remotePeer)
429
+ }
430
+
431
+ async #consumeIdentifyMessage (remotePeer: PeerId, message: Identify): Promise<SignedPeerRecord | undefined> {
432
+ if (message == null) {
433
+ throw new Error('Message was null or undefined')
434
+ }
435
+
436
+ log('received identify from %p', remotePeer)
437
+
438
+ if (message.signedPeerRecord == null) {
439
+ return
440
+ }
441
+
442
+ const envelope = await RecordEnvelope.openAndCertify(message.signedPeerRecord, PeerRecord.DOMAIN)
443
+ const peerRecord = PeerRecord.createFromProtobuf(envelope.payload)
444
+
445
+ // Verify peerId
446
+ if (!peerRecord.peerId.equals(envelope.peerId)) {
447
+ throw new Error('signing key does not match PeerId in the PeerRecord')
448
+ }
449
+
450
+ // Make sure remote peer is the one sending the record
451
+ if (!remotePeer.equals(peerRecord.peerId)) {
452
+ throw new Error('signing key does not match remote PeerId')
453
+ }
454
+
455
+ let peer: Peer | undefined
456
+
457
+ try {
458
+ peer = await this.peerStore.get(peerRecord.peerId)
459
+ } catch (err: any) {
460
+ if (err.code !== 'ERR_NOT_FOUND') {
461
+ throw err
462
+ }
463
+ }
464
+
465
+ log('received signedPeerRecord in push from %p', remotePeer)
466
+ let metadata = new Map()
467
+
468
+ if (peer?.peerRecordEnvelope != null) {
469
+ const storedEnvelope = await RecordEnvelope.createFromProtobuf(peer.peerRecordEnvelope)
470
+ const storedRecord = PeerRecord.createFromProtobuf(storedEnvelope.payload)
471
+
472
+ // ensure seq is greater than, or equal to, the last received
473
+ if (storedRecord.seqNumber >= peerRecord.seqNumber) {
474
+ log('sequence number was lower or equal to existing sequence number - stored: %d received: %d', storedRecord.seqNumber, peerRecord.seqNumber)
475
+ }
476
+
477
+ metadata = peer.metadata
478
+ }
479
+
480
+ if (message.agentVersion != null) {
481
+ metadata.set('AgentVersion', uint8ArrayFromString(message.agentVersion))
482
+ }
483
+
484
+ if (message.protocolVersion != null) {
485
+ metadata.set('ProtocolVersion', uint8ArrayFromString(message.protocolVersion))
486
+ }
487
+
488
+ await this.peerStore.patch(peerRecord.peerId, {
489
+ peerRecordEnvelope: message.signedPeerRecord,
490
+ protocols: message.protocols,
491
+ addresses: peerRecord.multiaddrs.map(multiaddr => ({
492
+ isCertified: true,
493
+ multiaddr
494
+ })),
495
+ metadata
496
+ })
497
+
498
+ log('consumed signedPeerRecord sent in push from %p', remotePeer)
499
+
500
+ return {
501
+ seq: peerRecord.seqNumber,
502
+ addresses: peerRecord.multiaddrs
503
+ }
504
+ }
505
+ }
506
+
507
+ /**
508
+ * Takes the `addr` and converts it to a Multiaddr if possible
509
+ */
510
+ function getCleanMultiaddr (addr: Uint8Array | string | null | undefined): Multiaddr | undefined {
511
+ if (addr != null && addr.length > 0) {
512
+ try {
513
+ return multiaddr(addr)
514
+ } catch {
515
+
516
+ }
517
+ }
518
+ }