@libp2p/identify 1.0.21-9d13a2f6a → 1.0.21
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/README.md +1 -24
- package/dist/index.min.js +3 -3
- package/dist/src/consts.d.ts +0 -2
- package/dist/src/consts.d.ts.map +1 -1
- package/dist/src/consts.js +0 -4
- package/dist/src/consts.js.map +1 -1
- package/dist/src/identify.d.ts +40 -3
- package/dist/src/identify.d.ts.map +1 -1
- package/dist/src/identify.js +334 -14
- package/dist/src/identify.js.map +1 -1
- package/dist/src/index.d.ts +17 -75
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +9 -28
- package/dist/src/index.js.map +1 -1
- package/dist/typedoc-urls.json +12 -0
- package/package.json +9 -10
- package/src/consts.ts +0 -6
- package/src/identify.ts +404 -16
- package/src/index.ts +20 -81
- package/dist/src/identify-push.d.ts +0 -18
- package/dist/src/identify-push.d.ts.map +0 -1
- package/dist/src/identify-push.js +0 -120
- package/dist/src/identify-push.js.map +0 -1
- package/dist/src/utils.d.ts +0 -53
- package/dist/src/utils.d.ts.map +0 -1
- package/dist/src/utils.js +0 -217
- package/dist/src/utils.js.map +0 -1
- package/src/identify-push.ts +0 -146
- package/src/utils.ts +0 -273
package/src/identify.ts
CHANGED
|
@@ -1,28 +1,94 @@
|
|
|
1
1
|
/* eslint-disable complexity */
|
|
2
2
|
|
|
3
|
-
import { CodeError, setMaxListeners } from '@libp2p/interface'
|
|
3
|
+
import { CodeError, ERR_NOT_FOUND, setMaxListeners } from '@libp2p/interface'
|
|
4
4
|
import { peerIdFromKeys } from '@libp2p/peer-id'
|
|
5
5
|
import { RecordEnvelope, PeerRecord } from '@libp2p/peer-record'
|
|
6
|
-
import { protocols } from '@multiformats/multiaddr'
|
|
6
|
+
import { type Multiaddr, multiaddr, protocols } from '@multiformats/multiaddr'
|
|
7
7
|
import { IP_OR_DOMAIN } from '@multiformats/multiaddr-matcher'
|
|
8
8
|
import { pbStream } from 'it-protobuf-stream'
|
|
9
|
+
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
10
|
+
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
11
|
+
import { isNode, isBrowser, isWebWorker, isElectronMain, isElectronRenderer, isReactNative } from 'wherearewe'
|
|
9
12
|
import {
|
|
13
|
+
IDENTIFY_PROTOCOL_VERSION,
|
|
10
14
|
MULTICODEC_IDENTIFY_PROTOCOL_NAME,
|
|
11
|
-
|
|
15
|
+
MULTICODEC_IDENTIFY_PUSH_PROTOCOL_NAME,
|
|
16
|
+
MULTICODEC_IDENTIFY_PROTOCOL_VERSION,
|
|
17
|
+
MULTICODEC_IDENTIFY_PUSH_PROTOCOL_VERSION
|
|
12
18
|
} from './consts.js'
|
|
13
19
|
import { Identify as IdentifyMessage } from './pb/message.js'
|
|
14
|
-
import { AbstractIdentify, consumeIdentifyMessage, defaultValues, getCleanMultiaddr } from './utils.js'
|
|
15
20
|
import type { Identify as IdentifyInterface, IdentifyComponents, IdentifyInit } from './index.js'
|
|
16
|
-
import type { IdentifyResult, AbortOptions, Connection, Stream, Startable } from '@libp2p/interface'
|
|
17
|
-
import type { IncomingStreamData } from '@libp2p/interface-internal'
|
|
21
|
+
import type { Libp2pEvents, IdentifyResult, SignedPeerRecord, AbortOptions, Logger, Connection, Stream, TypedEventTarget, PeerId, Peer, PeerData, PeerStore, Startable } from '@libp2p/interface'
|
|
22
|
+
import type { AddressManager, ConnectionManager, IncomingStreamData, Registrar } from '@libp2p/interface-internal'
|
|
23
|
+
|
|
24
|
+
// https://github.com/libp2p/go-libp2p/blob/8d2e54e1637041d5cf4fac1e531287560bd1f4ac/p2p/protocol/identify/id.go#L52
|
|
25
|
+
const MAX_IDENTIFY_MESSAGE_SIZE = 1024 * 8
|
|
26
|
+
|
|
27
|
+
const defaultValues = {
|
|
28
|
+
protocolPrefix: 'ipfs',
|
|
29
|
+
// https://github.com/libp2p/go-libp2p/blob/8d2e54e1637041d5cf4fac1e531287560bd1f4ac/p2p/protocol/identify/id.go#L48
|
|
30
|
+
timeout: 60000,
|
|
31
|
+
maxInboundStreams: 1,
|
|
32
|
+
maxOutboundStreams: 1,
|
|
33
|
+
maxPushIncomingStreams: 1,
|
|
34
|
+
maxPushOutgoingStreams: 1,
|
|
35
|
+
maxObservedAddresses: 10,
|
|
36
|
+
maxIdentifyMessageSize: 8192,
|
|
37
|
+
runOnConnectionOpen: true,
|
|
38
|
+
runOnTransientConnection: true
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export class Identify implements Startable, IdentifyInterface {
|
|
42
|
+
private readonly identifyProtocolStr: string
|
|
43
|
+
private readonly identifyPushProtocolStr: string
|
|
44
|
+
public readonly host: {
|
|
45
|
+
protocolVersion: string
|
|
46
|
+
agentVersion: string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private started: boolean
|
|
50
|
+
private readonly timeout: number
|
|
51
|
+
private readonly peerId: PeerId
|
|
52
|
+
private readonly peerStore: PeerStore
|
|
53
|
+
private readonly registrar: Registrar
|
|
54
|
+
private readonly connectionManager: ConnectionManager
|
|
55
|
+
private readonly addressManager: AddressManager
|
|
56
|
+
private readonly maxInboundStreams: number
|
|
57
|
+
private readonly maxOutboundStreams: number
|
|
58
|
+
private readonly maxPushIncomingStreams: number
|
|
59
|
+
private readonly maxPushOutgoingStreams: number
|
|
60
|
+
private readonly maxIdentifyMessageSize: number
|
|
61
|
+
private readonly maxObservedAddresses: number
|
|
62
|
+
private readonly events: TypedEventTarget<Libp2pEvents>
|
|
63
|
+
private readonly runOnTransientConnection: boolean
|
|
64
|
+
private readonly log: Logger
|
|
18
65
|
|
|
19
|
-
export class Identify extends AbstractIdentify implements Startable, IdentifyInterface {
|
|
20
66
|
constructor (components: IdentifyComponents, init: IdentifyInit = {}) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
67
|
+
this.started = false
|
|
68
|
+
this.peerId = components.peerId
|
|
69
|
+
this.peerStore = components.peerStore
|
|
70
|
+
this.registrar = components.registrar
|
|
71
|
+
this.addressManager = components.addressManager
|
|
72
|
+
this.connectionManager = components.connectionManager
|
|
73
|
+
this.events = components.events
|
|
74
|
+
this.log = components.logger.forComponent('libp2p:identify')
|
|
75
|
+
|
|
76
|
+
this.identifyProtocolStr = `/${init.protocolPrefix ?? defaultValues.protocolPrefix}/${MULTICODEC_IDENTIFY_PROTOCOL_NAME}/${MULTICODEC_IDENTIFY_PROTOCOL_VERSION}`
|
|
77
|
+
this.identifyPushProtocolStr = `/${init.protocolPrefix ?? defaultValues.protocolPrefix}/${MULTICODEC_IDENTIFY_PUSH_PROTOCOL_NAME}/${MULTICODEC_IDENTIFY_PUSH_PROTOCOL_VERSION}`
|
|
78
|
+
this.timeout = init.timeout ?? defaultValues.timeout
|
|
79
|
+
this.maxInboundStreams = init.maxInboundStreams ?? defaultValues.maxInboundStreams
|
|
80
|
+
this.maxOutboundStreams = init.maxOutboundStreams ?? defaultValues.maxOutboundStreams
|
|
81
|
+
this.maxPushIncomingStreams = init.maxPushIncomingStreams ?? defaultValues.maxPushIncomingStreams
|
|
82
|
+
this.maxPushOutgoingStreams = init.maxPushOutgoingStreams ?? defaultValues.maxPushOutgoingStreams
|
|
83
|
+
this.maxIdentifyMessageSize = init.maxIdentifyMessageSize ?? defaultValues.maxIdentifyMessageSize
|
|
84
|
+
this.maxObservedAddresses = init.maxObservedAddresses ?? defaultValues.maxObservedAddresses
|
|
85
|
+
this.runOnTransientConnection = init.runOnTransientConnection ?? defaultValues.runOnTransientConnection
|
|
86
|
+
|
|
87
|
+
// Store self host metadata
|
|
88
|
+
this.host = {
|
|
89
|
+
protocolVersion: `${init.protocolPrefix ?? defaultValues.protocolPrefix}/${IDENTIFY_PROTOCOL_VERSION}`,
|
|
90
|
+
agentVersion: init.agentVersion ?? `${components.nodeInfo.name}/${components.nodeInfo.version}`
|
|
91
|
+
}
|
|
26
92
|
|
|
27
93
|
if (init.runOnConnectionOpen ?? defaultValues.runOnConnectionOpen) {
|
|
28
94
|
// When a new connection happens, trigger identify
|
|
@@ -31,6 +97,151 @@ export class Identify extends AbstractIdentify implements Startable, IdentifyInt
|
|
|
31
97
|
this.identify(connection).catch(err => { this.log.error('error during identify trigged by connection:open', err) })
|
|
32
98
|
})
|
|
33
99
|
}
|
|
100
|
+
|
|
101
|
+
// When self peer record changes, trigger identify-push
|
|
102
|
+
components.events.addEventListener('self:peer:update', (evt) => {
|
|
103
|
+
void this.push().catch(err => { this.log.error(err) })
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
// Append user agent version to default AGENT_VERSION depending on the environment
|
|
107
|
+
if (this.host.agentVersion === `${components.nodeInfo.name}/${components.nodeInfo.version}`) {
|
|
108
|
+
if (isNode || isElectronMain) {
|
|
109
|
+
this.host.agentVersion += ` UserAgent=${globalThis.process.version}`
|
|
110
|
+
} else if (isBrowser || isWebWorker || isElectronRenderer || isReactNative) {
|
|
111
|
+
this.host.agentVersion += ` UserAgent=${globalThis.navigator.userAgent}`
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
isStarted (): boolean {
|
|
117
|
+
return this.started
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async start (): Promise<void> {
|
|
121
|
+
if (this.started) {
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
await this.peerStore.merge(this.peerId, {
|
|
126
|
+
metadata: {
|
|
127
|
+
AgentVersion: uint8ArrayFromString(this.host.agentVersion),
|
|
128
|
+
ProtocolVersion: uint8ArrayFromString(this.host.protocolVersion)
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
await this.registrar.handle(this.identifyProtocolStr, (data) => {
|
|
133
|
+
void this._handleIdentify(data).catch(err => {
|
|
134
|
+
this.log.error(err)
|
|
135
|
+
})
|
|
136
|
+
}, {
|
|
137
|
+
maxInboundStreams: this.maxInboundStreams,
|
|
138
|
+
maxOutboundStreams: this.maxOutboundStreams,
|
|
139
|
+
runOnTransientConnection: this.runOnTransientConnection
|
|
140
|
+
})
|
|
141
|
+
await this.registrar.handle(this.identifyPushProtocolStr, (data) => {
|
|
142
|
+
void this._handlePush(data).catch(err => {
|
|
143
|
+
this.log.error(err)
|
|
144
|
+
})
|
|
145
|
+
}, {
|
|
146
|
+
maxInboundStreams: this.maxPushIncomingStreams,
|
|
147
|
+
maxOutboundStreams: this.maxPushOutgoingStreams,
|
|
148
|
+
runOnTransientConnection: this.runOnTransientConnection
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
this.started = true
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async stop (): Promise<void> {
|
|
155
|
+
await this.registrar.unhandle(this.identifyProtocolStr)
|
|
156
|
+
await this.registrar.unhandle(this.identifyPushProtocolStr)
|
|
157
|
+
|
|
158
|
+
this.started = false
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Send an Identify Push update to the list of connections
|
|
163
|
+
*/
|
|
164
|
+
async pushToConnections (connections: Connection[]): Promise<void> {
|
|
165
|
+
const listenAddresses = this.addressManager.getAddresses().map(ma => ma.decapsulateCode(protocols('p2p').code))
|
|
166
|
+
const peerRecord = new PeerRecord({
|
|
167
|
+
peerId: this.peerId,
|
|
168
|
+
multiaddrs: listenAddresses
|
|
169
|
+
})
|
|
170
|
+
const signedPeerRecord = await RecordEnvelope.seal(peerRecord, this.peerId)
|
|
171
|
+
const supportedProtocols = this.registrar.getProtocols()
|
|
172
|
+
const peer = await this.peerStore.get(this.peerId)
|
|
173
|
+
const agentVersion = uint8ArrayToString(peer.metadata.get('AgentVersion') ?? uint8ArrayFromString(this.host.agentVersion))
|
|
174
|
+
const protocolVersion = uint8ArrayToString(peer.metadata.get('ProtocolVersion') ?? uint8ArrayFromString(this.host.protocolVersion))
|
|
175
|
+
|
|
176
|
+
const pushes = connections.map(async connection => {
|
|
177
|
+
let stream: Stream | undefined
|
|
178
|
+
const signal = AbortSignal.timeout(this.timeout)
|
|
179
|
+
|
|
180
|
+
setMaxListeners(Infinity, signal)
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
stream = await connection.newStream(this.identifyPushProtocolStr, {
|
|
184
|
+
signal,
|
|
185
|
+
runOnTransientConnection: this.runOnTransientConnection
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
const pb = pbStream(stream, {
|
|
189
|
+
maxDataLength: this.maxIdentifyMessageSize ?? MAX_IDENTIFY_MESSAGE_SIZE
|
|
190
|
+
}).pb(IdentifyMessage)
|
|
191
|
+
|
|
192
|
+
await pb.write({
|
|
193
|
+
listenAddrs: listenAddresses.map(ma => ma.bytes),
|
|
194
|
+
signedPeerRecord: signedPeerRecord.marshal(),
|
|
195
|
+
protocols: supportedProtocols,
|
|
196
|
+
agentVersion,
|
|
197
|
+
protocolVersion
|
|
198
|
+
}, {
|
|
199
|
+
signal
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
await stream.close({
|
|
203
|
+
signal
|
|
204
|
+
})
|
|
205
|
+
} catch (err: any) {
|
|
206
|
+
// Just log errors
|
|
207
|
+
this.log.error('could not push identify update to peer', err)
|
|
208
|
+
stream?.abort(err)
|
|
209
|
+
}
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
await Promise.all(pushes)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Calls `push` on all peer connections
|
|
217
|
+
*/
|
|
218
|
+
async push (): Promise<void> {
|
|
219
|
+
// Do not try to push if we are not running
|
|
220
|
+
if (!this.isStarted()) {
|
|
221
|
+
return
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const connections: Connection[] = []
|
|
225
|
+
|
|
226
|
+
await Promise.all(
|
|
227
|
+
this.connectionManager.getConnections().map(async conn => {
|
|
228
|
+
try {
|
|
229
|
+
const peer = await this.peerStore.get(conn.remotePeer)
|
|
230
|
+
|
|
231
|
+
if (!peer.protocols.includes(this.identifyPushProtocolStr)) {
|
|
232
|
+
return
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
connections.push(conn)
|
|
236
|
+
} catch (err: any) {
|
|
237
|
+
if (err.code !== ERR_NOT_FOUND) {
|
|
238
|
+
throw err
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
})
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
await this.pushToConnections(connections)
|
|
34
245
|
}
|
|
35
246
|
|
|
36
247
|
async _identify (connection: Connection, options: AbortOptions = {}): Promise<IdentifyMessage> {
|
|
@@ -47,13 +258,13 @@ export class Identify extends AbstractIdentify implements Startable, IdentifyInt
|
|
|
47
258
|
}
|
|
48
259
|
|
|
49
260
|
try {
|
|
50
|
-
stream = await connection.newStream(this.
|
|
261
|
+
stream = await connection.newStream(this.identifyProtocolStr, {
|
|
51
262
|
...options,
|
|
52
263
|
runOnTransientConnection: this.runOnTransientConnection
|
|
53
264
|
})
|
|
54
265
|
|
|
55
266
|
const pb = pbStream(stream, {
|
|
56
|
-
maxDataLength: this.
|
|
267
|
+
maxDataLength: this.maxIdentifyMessageSize ?? MAX_IDENTIFY_MESSAGE_SIZE
|
|
57
268
|
}).pb(IdentifyMessage)
|
|
58
269
|
|
|
59
270
|
const message = await pb.read(options)
|
|
@@ -102,14 +313,14 @@ export class Identify extends AbstractIdentify implements Startable, IdentifyInt
|
|
|
102
313
|
this.addressManager.addObservedAddr(cleanObservedAddr)
|
|
103
314
|
}
|
|
104
315
|
|
|
105
|
-
return consumeIdentifyMessage(
|
|
316
|
+
return this.#consumeIdentifyMessage(connection, message)
|
|
106
317
|
}
|
|
107
318
|
|
|
108
319
|
/**
|
|
109
320
|
* Sends the `Identify` response with the Signed Peer Record
|
|
110
321
|
* to the requesting peer over the given `connection`
|
|
111
322
|
*/
|
|
112
|
-
async
|
|
323
|
+
async _handleIdentify (data: IncomingStreamData): Promise<void> {
|
|
113
324
|
const { connection, stream } = data
|
|
114
325
|
|
|
115
326
|
const signal = AbortSignal.timeout(this.timeout)
|
|
@@ -160,4 +371,181 @@ export class Identify extends AbstractIdentify implements Startable, IdentifyInt
|
|
|
160
371
|
stream.abort(err)
|
|
161
372
|
}
|
|
162
373
|
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Reads the Identify Push message from the given `connection`
|
|
377
|
+
*/
|
|
378
|
+
async _handlePush (data: IncomingStreamData): Promise<void> {
|
|
379
|
+
const { connection, stream } = data
|
|
380
|
+
|
|
381
|
+
try {
|
|
382
|
+
if (this.peerId.equals(connection.remotePeer)) {
|
|
383
|
+
throw new Error('received push from ourselves?')
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const options = {
|
|
387
|
+
signal: AbortSignal.timeout(this.timeout)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const pb = pbStream(stream, {
|
|
391
|
+
maxDataLength: this.maxIdentifyMessageSize ?? MAX_IDENTIFY_MESSAGE_SIZE
|
|
392
|
+
}).pb(IdentifyMessage)
|
|
393
|
+
|
|
394
|
+
const message = await pb.read(options)
|
|
395
|
+
await stream.close(options)
|
|
396
|
+
|
|
397
|
+
await this.#consumeIdentifyMessage(connection, message)
|
|
398
|
+
} catch (err: any) {
|
|
399
|
+
this.log.error('received invalid message', err)
|
|
400
|
+
stream.abort(err)
|
|
401
|
+
return
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
this.log('handled push from %p', connection.remotePeer)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
async #consumeIdentifyMessage (connection: Connection, message: IdentifyMessage): Promise<IdentifyResult> {
|
|
408
|
+
this.log('received identify from %p', connection.remotePeer)
|
|
409
|
+
|
|
410
|
+
if (message == null) {
|
|
411
|
+
throw new CodeError('message was null or undefined', 'ERR_INVALID_MESSAGE')
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const peer: PeerData = {}
|
|
415
|
+
|
|
416
|
+
if (message.listenAddrs.length > 0) {
|
|
417
|
+
peer.addresses = message.listenAddrs.map(buf => ({
|
|
418
|
+
isCertified: false,
|
|
419
|
+
multiaddr: multiaddr(buf)
|
|
420
|
+
}))
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (message.protocols.length > 0) {
|
|
424
|
+
peer.protocols = message.protocols
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (message.publicKey != null) {
|
|
428
|
+
peer.publicKey = message.publicKey
|
|
429
|
+
|
|
430
|
+
const peerId = await peerIdFromKeys(message.publicKey)
|
|
431
|
+
|
|
432
|
+
if (!peerId.equals(connection.remotePeer)) {
|
|
433
|
+
throw new CodeError('public key did not match remote PeerId', 'ERR_INVALID_PUBLIC_KEY')
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
let output: SignedPeerRecord | undefined
|
|
438
|
+
|
|
439
|
+
// if the peer record has been sent, prefer the addresses in the record as they are signed by the remote peer
|
|
440
|
+
if (message.signedPeerRecord != null) {
|
|
441
|
+
this.log('received signedPeerRecord from %p', connection.remotePeer)
|
|
442
|
+
|
|
443
|
+
let peerRecordEnvelope = message.signedPeerRecord
|
|
444
|
+
const envelope = await RecordEnvelope.openAndCertify(peerRecordEnvelope, PeerRecord.DOMAIN)
|
|
445
|
+
let peerRecord = PeerRecord.createFromProtobuf(envelope.payload)
|
|
446
|
+
|
|
447
|
+
// Verify peerId
|
|
448
|
+
if (!peerRecord.peerId.equals(envelope.peerId)) {
|
|
449
|
+
throw new CodeError('signing key does not match PeerId in the PeerRecord', 'ERR_INVALID_SIGNING_KEY')
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Make sure remote peer is the one sending the record
|
|
453
|
+
if (!connection.remotePeer.equals(peerRecord.peerId)) {
|
|
454
|
+
throw new CodeError('signing key does not match remote PeerId', 'ERR_INVALID_PEER_RECORD_KEY')
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
let existingPeer: Peer | undefined
|
|
458
|
+
|
|
459
|
+
try {
|
|
460
|
+
existingPeer = await this.peerStore.get(peerRecord.peerId)
|
|
461
|
+
} catch (err: any) {
|
|
462
|
+
if (err.code !== 'ERR_NOT_FOUND') {
|
|
463
|
+
throw err
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (existingPeer != null) {
|
|
468
|
+
// don't lose any existing metadata
|
|
469
|
+
peer.metadata = existingPeer.metadata
|
|
470
|
+
|
|
471
|
+
// if we have previously received a signed record for this peer, compare it to the incoming one
|
|
472
|
+
if (existingPeer.peerRecordEnvelope != null) {
|
|
473
|
+
const storedEnvelope = await RecordEnvelope.createFromProtobuf(existingPeer.peerRecordEnvelope)
|
|
474
|
+
const storedRecord = PeerRecord.createFromProtobuf(storedEnvelope.payload)
|
|
475
|
+
|
|
476
|
+
// ensure seq is greater than, or equal to, the last received
|
|
477
|
+
if (storedRecord.seqNumber >= peerRecord.seqNumber) {
|
|
478
|
+
this.log('sequence number was lower or equal to existing sequence number - stored: %d received: %d', storedRecord.seqNumber, peerRecord.seqNumber)
|
|
479
|
+
peerRecord = storedRecord
|
|
480
|
+
peerRecordEnvelope = existingPeer.peerRecordEnvelope
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// store the signed record for next time
|
|
486
|
+
peer.peerRecordEnvelope = peerRecordEnvelope
|
|
487
|
+
|
|
488
|
+
// override the stored addresses with the signed multiaddrs
|
|
489
|
+
peer.addresses = peerRecord.multiaddrs.map(multiaddr => ({
|
|
490
|
+
isCertified: true,
|
|
491
|
+
multiaddr
|
|
492
|
+
}))
|
|
493
|
+
|
|
494
|
+
output = {
|
|
495
|
+
seq: peerRecord.seqNumber,
|
|
496
|
+
addresses: peerRecord.multiaddrs
|
|
497
|
+
}
|
|
498
|
+
} else {
|
|
499
|
+
this.log('%p did not send a signed peer record', connection.remotePeer)
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
this.log('patching %p with', connection.remotePeer, peer)
|
|
503
|
+
await this.peerStore.patch(connection.remotePeer, peer)
|
|
504
|
+
|
|
505
|
+
if (message.agentVersion != null || message.protocolVersion != null) {
|
|
506
|
+
const metadata: Record<string, Uint8Array> = {}
|
|
507
|
+
|
|
508
|
+
if (message.agentVersion != null) {
|
|
509
|
+
metadata.AgentVersion = uint8ArrayFromString(message.agentVersion)
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (message.protocolVersion != null) {
|
|
513
|
+
metadata.ProtocolVersion = uint8ArrayFromString(message.protocolVersion)
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
this.log('merging %p metadata', connection.remotePeer, metadata)
|
|
517
|
+
await this.peerStore.merge(connection.remotePeer, {
|
|
518
|
+
metadata
|
|
519
|
+
})
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const result: IdentifyResult = {
|
|
523
|
+
peerId: connection.remotePeer,
|
|
524
|
+
protocolVersion: message.protocolVersion,
|
|
525
|
+
agentVersion: message.agentVersion,
|
|
526
|
+
publicKey: message.publicKey,
|
|
527
|
+
listenAddrs: message.listenAddrs.map(buf => multiaddr(buf)),
|
|
528
|
+
observedAddr: message.observedAddr == null ? undefined : multiaddr(message.observedAddr),
|
|
529
|
+
protocols: message.protocols,
|
|
530
|
+
signedPeerRecord: output,
|
|
531
|
+
connection
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
this.events.safeDispatchEvent('peer:identify', { detail: result })
|
|
535
|
+
|
|
536
|
+
return result
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Takes the `addr` and converts it to a Multiaddr if possible
|
|
542
|
+
*/
|
|
543
|
+
function getCleanMultiaddr (addr: Uint8Array | string | null | undefined): Multiaddr | undefined {
|
|
544
|
+
if (addr != null && addr.length > 0) {
|
|
545
|
+
try {
|
|
546
|
+
return multiaddr(addr)
|
|
547
|
+
} catch {
|
|
548
|
+
|
|
549
|
+
}
|
|
550
|
+
}
|
|
163
551
|
}
|
package/src/index.ts
CHANGED
|
@@ -3,16 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Use the `identify` function to add support for the [Identify protocol](https://github.com/libp2p/specs/blob/master/identify/README.md) to libp2p.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* A second function, `identifyPush` is also exported to add support for [identify/push](https://github.com/libp2p/specs/blob/master/identify/README.md#identifypush).
|
|
9
|
-
*
|
|
10
|
-
* This protocol will send updates to all connected peers when the multiaddrs or protocols of the current node change.
|
|
11
|
-
*
|
|
12
|
-
* > [!TIP]
|
|
13
|
-
* > For maximum network compatibility you should configure both protocols
|
|
14
|
-
*
|
|
15
|
-
* @example Enabling identify
|
|
6
|
+
* @example
|
|
16
7
|
*
|
|
17
8
|
* ```typescript
|
|
18
9
|
* import { createLibp2p } from 'libp2p'
|
|
@@ -25,32 +16,19 @@
|
|
|
25
16
|
* }
|
|
26
17
|
* })
|
|
27
18
|
* ```
|
|
28
|
-
*
|
|
29
|
-
* @example Enabling identify push
|
|
30
|
-
*
|
|
31
|
-
* ```typescript
|
|
32
|
-
* import { createLibp2p } from 'libp2p'
|
|
33
|
-
* import { identifyPush } from '@libp2p/identify'
|
|
34
|
-
*
|
|
35
|
-
* const node = await createLibp2p({
|
|
36
|
-
* // ...other options
|
|
37
|
-
* services: {
|
|
38
|
-
* identifyPush: identifyPush()
|
|
39
|
-
* }
|
|
40
|
-
* })
|
|
41
|
-
* ```
|
|
42
19
|
*/
|
|
43
20
|
|
|
44
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
MULTICODEC_IDENTIFY,
|
|
23
|
+
MULTICODEC_IDENTIFY_PUSH
|
|
24
|
+
} from './consts.js'
|
|
45
25
|
import { Identify as IdentifyClass } from './identify.js'
|
|
46
26
|
import type { AbortOptions, IdentifyResult, Libp2pEvents, ComponentLogger, NodeInfo, TypedEventTarget, PeerId, PeerStore, Connection } from '@libp2p/interface'
|
|
47
27
|
import type { AddressManager, ConnectionManager, Registrar } from '@libp2p/interface-internal'
|
|
48
28
|
|
|
49
29
|
export interface IdentifyInit {
|
|
50
30
|
/**
|
|
51
|
-
* The prefix to use for the protocol
|
|
52
|
-
*
|
|
53
|
-
* @default 'ipfs'
|
|
31
|
+
* The prefix to use for the protocol (default: 'ipfs')
|
|
54
32
|
*/
|
|
55
33
|
protocolPrefix?: string
|
|
56
34
|
|
|
@@ -61,73 +39,36 @@ export interface IdentifyInit {
|
|
|
61
39
|
|
|
62
40
|
/**
|
|
63
41
|
* How long we should wait for a remote peer to send their identify response
|
|
64
|
-
*
|
|
65
|
-
* @default 5000
|
|
66
42
|
*/
|
|
67
43
|
timeout?: number
|
|
68
44
|
|
|
69
45
|
/**
|
|
70
|
-
* Identify responses larger than this in bytes will be rejected
|
|
71
|
-
*
|
|
72
|
-
* @default 8192
|
|
46
|
+
* Identify responses larger than this in bytes will be rejected (default: 8192)
|
|
73
47
|
*/
|
|
74
|
-
|
|
48
|
+
maxIdentifyMessageSize?: number
|
|
75
49
|
|
|
76
|
-
/**
|
|
77
|
-
* The maximum number of inbound streams that may be open on a single
|
|
78
|
-
* connection for this protocol
|
|
79
|
-
*
|
|
80
|
-
* @default 1
|
|
81
|
-
*/
|
|
82
50
|
maxInboundStreams?: number
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* The maximum number of outbound streams that may be open on a single
|
|
86
|
-
* connection for this protocol
|
|
87
|
-
*
|
|
88
|
-
* @default 1
|
|
89
|
-
*/
|
|
90
51
|
maxOutboundStreams?: number
|
|
91
52
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
*/
|
|
53
|
+
maxPushIncomingStreams?: number
|
|
54
|
+
maxPushOutgoingStreams?: number
|
|
95
55
|
maxObservedAddresses?: number
|
|
96
56
|
|
|
97
57
|
/**
|
|
98
|
-
* Whether to
|
|
99
|
-
*
|
|
100
|
-
* @default true
|
|
101
|
-
*/
|
|
102
|
-
runOnTransientConnection?: boolean
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Whether to automatically run identify on newly opened connections
|
|
106
|
-
*
|
|
107
|
-
* @default true
|
|
58
|
+
* Whether to automatically dial identify on newly opened connections (default: true)
|
|
108
59
|
*/
|
|
109
60
|
runOnConnectionOpen?: boolean
|
|
110
|
-
}
|
|
111
61
|
|
|
112
|
-
export interface IdentifyPushInit extends Omit<IdentifyInit, 'runOnConnectionOpen'> {
|
|
113
62
|
/**
|
|
114
|
-
* Whether to
|
|
115
|
-
*
|
|
116
|
-
* @default true
|
|
63
|
+
* Whether to run on connections with data or duration limits (default: true)
|
|
117
64
|
*/
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Push to this many connections in parallel
|
|
122
|
-
*
|
|
123
|
-
* @default 32
|
|
124
|
-
*/
|
|
125
|
-
concurrency?: number
|
|
65
|
+
runOnTransientConnection?: boolean
|
|
126
66
|
}
|
|
127
67
|
|
|
128
68
|
export interface IdentifyComponents {
|
|
129
69
|
peerId: PeerId
|
|
130
70
|
peerStore: PeerStore
|
|
71
|
+
connectionManager: ConnectionManager
|
|
131
72
|
registrar: Registrar
|
|
132
73
|
addressManager: AddressManager
|
|
133
74
|
events: TypedEventTarget<Libp2pEvents>
|
|
@@ -135,8 +76,12 @@ export interface IdentifyComponents {
|
|
|
135
76
|
nodeInfo: NodeInfo
|
|
136
77
|
}
|
|
137
78
|
|
|
138
|
-
|
|
139
|
-
|
|
79
|
+
/**
|
|
80
|
+
* The protocols the Identify service supports
|
|
81
|
+
*/
|
|
82
|
+
export const multicodecs = {
|
|
83
|
+
IDENTIFY: MULTICODEC_IDENTIFY,
|
|
84
|
+
IDENTIFY_PUSH: MULTICODEC_IDENTIFY_PUSH
|
|
140
85
|
}
|
|
141
86
|
|
|
142
87
|
export interface Identify {
|
|
@@ -148,16 +93,10 @@ export interface Identify {
|
|
|
148
93
|
* you may be better off configuring a topology to be notified instead.
|
|
149
94
|
*/
|
|
150
95
|
identify(connection: Connection, options?: AbortOptions): Promise<IdentifyResult>
|
|
151
|
-
}
|
|
152
96
|
|
|
153
|
-
export interface IdentifyPush {
|
|
154
97
|
push(): Promise<void>
|
|
155
98
|
}
|
|
156
99
|
|
|
157
100
|
export function identify (init: IdentifyInit = {}): (components: IdentifyComponents) => Identify {
|
|
158
101
|
return (components) => new IdentifyClass(components, init)
|
|
159
102
|
}
|
|
160
|
-
|
|
161
|
-
export function identifyPush (init: IdentifyPushInit = {}): (components: IdentifyPushComponents) => IdentifyPush {
|
|
162
|
-
return (components) => new IdentifyPushClass(components, init)
|
|
163
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { AbstractIdentify } from './utils.js';
|
|
2
|
-
import type { IdentifyPush as IdentifyPushInterface, IdentifyPushComponents, IdentifyPushInit } from './index.js';
|
|
3
|
-
import type { Startable } from '@libp2p/interface';
|
|
4
|
-
import type { IncomingStreamData } from '@libp2p/interface-internal';
|
|
5
|
-
export declare class IdentifyPush extends AbstractIdentify implements Startable, IdentifyPushInterface {
|
|
6
|
-
private readonly connectionManager;
|
|
7
|
-
private readonly concurrency;
|
|
8
|
-
constructor(components: IdentifyPushComponents, init?: IdentifyPushInit);
|
|
9
|
-
/**
|
|
10
|
-
* Calls `push` on all peer connections
|
|
11
|
-
*/
|
|
12
|
-
push(): Promise<void>;
|
|
13
|
-
/**
|
|
14
|
-
* Reads the Identify Push message from the given `connection`
|
|
15
|
-
*/
|
|
16
|
-
handleProtocol(data: IncomingStreamData): Promise<void>;
|
|
17
|
-
}
|
|
18
|
-
//# sourceMappingURL=identify-push.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"identify-push.d.ts","sourceRoot":"","sources":["../../src/identify-push.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,gBAAgB,EAAyC,MAAM,YAAY,CAAA;AACpF,OAAO,KAAK,EAAE,YAAY,IAAI,qBAAqB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AACjH,OAAO,KAAK,EAAU,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC1D,OAAO,KAAK,EAAqB,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAEvF,qBAAa,YAAa,SAAQ,gBAAiB,YAAW,SAAS,EAAE,qBAAqB;IAC5F,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAmB;IACrD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;gBAEvB,UAAU,EAAE,sBAAsB,EAAE,IAAI,GAAE,gBAAqB;IAkB5E;;OAEG;IACG,IAAI,IAAK,OAAO,CAAC,IAAI,CAAC;IAqE5B;;OAEG;IACG,cAAc,CAAE,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;CA4B/D"}
|