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.
- package/dist/index.min.js +30 -30
- package/dist/src/address-manager/index.d.ts +8 -4
- package/dist/src/address-manager/index.d.ts.map +1 -1
- package/dist/src/address-manager/index.js +37 -6
- package/dist/src/address-manager/index.js.map +1 -1
- package/dist/src/address-manager/utils.d.ts +2 -0
- package/dist/src/address-manager/utils.d.ts.map +1 -0
- package/dist/src/address-manager/utils.js +12 -0
- package/dist/src/address-manager/utils.js.map +1 -0
- package/dist/src/autonat/constants.d.ts +16 -2
- package/dist/src/autonat/constants.d.ts.map +1 -1
- package/dist/src/autonat/constants.js +16 -2
- package/dist/src/autonat/constants.js.map +1 -1
- package/dist/src/autonat/index.d.ts +12 -33
- package/dist/src/autonat/index.d.ts.map +1 -1
- package/dist/src/autonat/index.js +36 -30
- package/dist/src/autonat/index.js.map +1 -1
- package/dist/src/circuit-relay/server/advert-service.d.ts.map +1 -1
- package/dist/src/circuit-relay/server/advert-service.js +2 -2
- package/dist/src/circuit-relay/server/advert-service.js.map +1 -1
- package/dist/src/circuit-relay/server/index.d.ts.map +1 -1
- package/dist/src/circuit-relay/server/index.js +10 -3
- package/dist/src/circuit-relay/server/index.js.map +1 -1
- package/dist/src/circuit-relay/transport/discovery.d.ts.map +1 -1
- package/dist/src/circuit-relay/transport/discovery.js +4 -2
- package/dist/src/circuit-relay/transport/discovery.js.map +1 -1
- package/dist/src/circuit-relay/transport/index.d.ts +3 -0
- package/dist/src/circuit-relay/transport/index.d.ts.map +1 -1
- package/dist/src/circuit-relay/transport/index.js +6 -4
- package/dist/src/circuit-relay/transport/index.js.map +1 -1
- package/dist/src/circuit-relay/transport/listener.d.ts.map +1 -1
- package/dist/src/circuit-relay/transport/listener.js +30 -26
- package/dist/src/circuit-relay/transport/listener.js.map +1 -1
- package/dist/src/circuit-relay/transport/reservation-store.d.ts +5 -4
- package/dist/src/circuit-relay/transport/reservation-store.d.ts.map +1 -1
- package/dist/src/circuit-relay/transport/reservation-store.js +37 -30
- package/dist/src/circuit-relay/transport/reservation-store.js.map +1 -1
- package/dist/src/components.d.ts +6 -63
- package/dist/src/components.d.ts.map +1 -1
- package/dist/src/components.js +60 -164
- package/dist/src/components.js.map +1 -1
- package/dist/src/config/connection-gater.browser.d.ts +8 -0
- package/dist/src/config/connection-gater.browser.d.ts.map +1 -0
- package/dist/src/config/connection-gater.browser.js +27 -0
- package/dist/src/config/connection-gater.browser.js.map +1 -0
- package/dist/src/config/connection-gater.d.ts +6 -0
- package/dist/src/config/connection-gater.d.ts.map +1 -0
- package/dist/src/config/connection-gater.js +18 -0
- package/dist/src/config/connection-gater.js.map +1 -0
- package/dist/src/config.d.ts +2 -1
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +0 -62
- package/dist/src/config.js.map +1 -1
- package/dist/src/connection-manager/auto-dial.d.ts +3 -0
- package/dist/src/connection-manager/auto-dial.d.ts.map +1 -1
- package/dist/src/connection-manager/auto-dial.js +36 -13
- package/dist/src/connection-manager/auto-dial.js.map +1 -1
- package/dist/src/connection-manager/connection-pruner.d.ts +4 -0
- package/dist/src/connection-manager/connection-pruner.d.ts.map +1 -1
- package/dist/src/connection-manager/connection-pruner.js +23 -8
- package/dist/src/connection-manager/connection-pruner.js.map +1 -1
- package/dist/src/connection-manager/dial-queue.d.ts +2 -1
- package/dist/src/connection-manager/dial-queue.d.ts.map +1 -1
- package/dist/src/connection-manager/dial-queue.js +36 -14
- package/dist/src/connection-manager/dial-queue.js.map +1 -1
- package/dist/src/connection-manager/index.d.ts +8 -7
- package/dist/src/connection-manager/index.d.ts.map +1 -1
- package/dist/src/connection-manager/index.js +23 -34
- package/dist/src/connection-manager/index.js.map +1 -1
- package/dist/src/content-routing/index.d.ts +0 -10
- package/dist/src/content-routing/index.d.ts.map +1 -1
- package/dist/src/content-routing/index.js +7 -42
- package/dist/src/content-routing/index.js.map +1 -1
- package/dist/src/content-routing/utils.d.ts.map +1 -1
- package/dist/src/content-routing/utils.js +3 -1
- package/dist/src/content-routing/utils.js.map +1 -1
- package/dist/src/fetch/index.d.ts +13 -35
- package/dist/src/fetch/index.d.ts.map +1 -1
- package/dist/src/fetch/index.js +7 -9
- package/dist/src/fetch/index.js.map +1 -1
- package/dist/src/identify/identify.d.ts +53 -0
- package/dist/src/identify/identify.d.ts.map +1 -0
- package/dist/src/identify/identify.js +405 -0
- package/dist/src/identify/identify.js.map +1 -0
- package/dist/src/identify/index.d.ts +12 -58
- package/dist/src/identify/index.d.ts.map +1 -1
- package/dist/src/identify/index.js +5 -388
- package/dist/src/identify/index.js.map +1 -1
- package/dist/src/index.d.ts +13 -68
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/insecure/index.js.map +1 -1
- package/dist/src/libp2p.d.ts +11 -42
- package/dist/src/libp2p.d.ts.map +1 -1
- package/dist/src/libp2p.js +137 -196
- package/dist/src/libp2p.js.map +1 -1
- package/dist/src/peer-routing.d.ts +1 -38
- package/dist/src/peer-routing.d.ts.map +1 -1
- package/dist/src/peer-routing.js +0 -59
- package/dist/src/peer-routing.js.map +1 -1
- package/dist/src/ping/constants.d.ts +4 -0
- package/dist/src/ping/constants.d.ts.map +1 -1
- package/dist/src/ping/constants.js +10 -0
- package/dist/src/ping/constants.js.map +1 -1
- package/dist/src/ping/index.d.ts +10 -27
- package/dist/src/ping/index.d.ts.map +1 -1
- package/dist/src/ping/index.js +18 -21
- package/dist/src/ping/index.js.map +1 -1
- package/dist/src/registrar.d.ts +5 -2
- package/dist/src/registrar.d.ts.map +1 -1
- package/dist/src/registrar.js +27 -25
- package/dist/src/registrar.js.map +1 -1
- package/dist/src/transport-manager.d.ts +11 -4
- package/dist/src/transport-manager.d.ts.map +1 -1
- package/dist/src/transport-manager.js +19 -10
- package/dist/src/transport-manager.js.map +1 -1
- package/dist/src/upgrader.d.ts +15 -10
- package/dist/src/upgrader.d.ts.map +1 -1
- package/dist/src/upgrader.js +39 -48
- package/dist/src/upgrader.js.map +1 -1
- package/dist/src/upnp-nat/index.d.ts +42 -0
- package/dist/src/upnp-nat/index.d.ts.map +1 -0
- package/dist/src/{nat-manager.js → upnp-nat/index.js} +11 -7
- package/dist/src/upnp-nat/index.js.map +1 -0
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.d.ts.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/dist/typedoc-urls.json +25 -10
- package/package.json +61 -45
- package/src/address-manager/index.ts +50 -8
- package/src/address-manager/utils.ts +14 -0
- package/src/autonat/constants.ts +17 -2
- package/src/autonat/index.ts +53 -45
- package/src/circuit-relay/server/advert-service.ts +2 -2
- package/src/circuit-relay/server/index.ts +11 -3
- package/src/circuit-relay/transport/discovery.ts +5 -3
- package/src/circuit-relay/transport/index.ts +9 -4
- package/src/circuit-relay/transport/listener.ts +38 -28
- package/src/circuit-relay/transport/reservation-store.ts +34 -20
- package/src/components.ts +71 -234
- package/src/config/connection-gater.browser.ts +31 -0
- package/src/config/connection-gater.ts +19 -0
- package/src/config.ts +3 -64
- package/src/connection-manager/auto-dial.ts +44 -15
- package/src/connection-manager/connection-pruner.ts +28 -8
- package/src/connection-manager/dial-queue.ts +41 -21
- package/src/connection-manager/index.ts +31 -43
- package/src/content-routing/index.ts +7 -55
- package/src/content-routing/utils.ts +3 -1
- package/src/fetch/index.ts +51 -14
- package/src/identify/identify.ts +518 -0
- package/src/identify/index.ts +17 -507
- package/src/index.ts +14 -77
- package/src/insecure/index.ts +4 -4
- package/src/libp2p.ts +139 -260
- package/src/peer-routing.ts +1 -101
- package/src/ping/constants.ts +11 -0
- package/src/ping/index.ts +32 -28
- package/src/registrar.ts +31 -27
- package/src/transport-manager.ts +27 -13
- package/src/upgrader.ts +62 -58
- package/src/{nat-manager.ts → upnp-nat/index.ts} +16 -17
- package/src/version.ts +1 -1
- package/dist/src/dht/dht-content-routing.d.ts +0 -17
- package/dist/src/dht/dht-content-routing.d.ts.map +0 -1
- package/dist/src/dht/dht-content-routing.js +0 -32
- package/dist/src/dht/dht-content-routing.js.map +0 -1
- package/dist/src/dht/dht-peer-routing.d.ts +0 -15
- package/dist/src/dht/dht-peer-routing.d.ts.map +0 -1
- package/dist/src/dht/dht-peer-routing.js +0 -26
- package/dist/src/dht/dht-peer-routing.js.map +0 -1
- package/dist/src/dht/dummy-dht.d.ts +0 -20
- package/dist/src/dht/dummy-dht.d.ts.map +0 -1
- package/dist/src/dht/dummy-dht.js +0 -46
- package/dist/src/dht/dummy-dht.js.map +0 -1
- package/dist/src/nat-manager.d.ts +0 -74
- package/dist/src/nat-manager.d.ts.map +0 -1
- package/dist/src/nat-manager.js.map +0 -1
- package/dist/src/peer-record-updater.d.ts +0 -24
- package/dist/src/peer-record-updater.d.ts.map +0 -1
- package/dist/src/peer-record-updater.js +0 -44
- package/dist/src/peer-record-updater.js.map +0 -1
- package/src/dht/dht-content-routing.ts +0 -44
- package/src/dht/dht-peer-routing.ts +0 -36
- package/src/dht/dummy-dht.ts +0 -60
- 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
|
+
}
|