libp2p 0.42.2-593deefe → 0.42.2-5e63ee2d

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 (228) hide show
  1. package/dist/index.min.js +30 -30
  2. package/dist/src/address-manager/index.d.ts +1 -10
  3. package/dist/src/address-manager/index.d.ts.map +1 -1
  4. package/dist/src/address-manager/index.js +46 -29
  5. package/dist/src/address-manager/index.js.map +1 -1
  6. package/dist/src/autonat/constants.d.ts +4 -0
  7. package/dist/src/autonat/constants.d.ts.map +1 -0
  8. package/dist/src/autonat/constants.js +4 -0
  9. package/dist/src/autonat/constants.js.map +1 -0
  10. package/dist/src/autonat/index.d.ts +64 -0
  11. package/dist/src/autonat/index.d.ts.map +1 -0
  12. package/dist/src/autonat/index.js +417 -0
  13. package/dist/src/autonat/index.js.map +1 -0
  14. package/dist/src/autonat/pb/index.d.ts +57 -0
  15. package/dist/src/autonat/pb/index.d.ts.map +1 -0
  16. package/dist/src/autonat/pb/index.js +250 -0
  17. package/dist/src/autonat/pb/index.js.map +1 -0
  18. package/dist/src/circuit/constants.d.ts +29 -3
  19. package/dist/src/circuit/constants.d.ts.map +1 -1
  20. package/dist/src/circuit/constants.js +32 -5
  21. package/dist/src/circuit/constants.js.map +1 -1
  22. package/dist/src/circuit/index.d.ts +16 -61
  23. package/dist/src/circuit/index.d.ts.map +1 -1
  24. package/dist/src/circuit/index.js +2 -1
  25. package/dist/src/circuit/index.js.map +1 -1
  26. package/dist/src/circuit/server/advert-service.d.ts +43 -0
  27. package/dist/src/circuit/server/advert-service.d.ts.map +1 -0
  28. package/dist/src/circuit/server/advert-service.js +69 -0
  29. package/dist/src/circuit/server/advert-service.js.map +1 -0
  30. package/dist/src/circuit/server/index.d.ts +60 -0
  31. package/dist/src/circuit/server/index.d.ts.map +1 -0
  32. package/dist/src/circuit/server/index.js +287 -0
  33. package/dist/src/circuit/server/index.js.map +1 -0
  34. package/dist/src/circuit/{reservation-store.d.ts → server/reservation-store.d.ts} +17 -10
  35. package/dist/src/circuit/server/reservation-store.d.ts.map +1 -0
  36. package/dist/src/circuit/server/reservation-store.js +57 -0
  37. package/dist/src/circuit/server/reservation-store.js.map +1 -0
  38. package/dist/src/circuit/server/reservation-voucher.d.ts.map +1 -0
  39. package/dist/src/circuit/{reservation-voucher.js → server/reservation-voucher.js} +1 -1
  40. package/dist/src/circuit/server/reservation-voucher.js.map +1 -0
  41. package/dist/src/circuit/transport/discovery.d.ts +45 -0
  42. package/dist/src/circuit/transport/discovery.d.ts.map +1 -0
  43. package/dist/src/circuit/transport/discovery.js +82 -0
  44. package/dist/src/circuit/transport/discovery.js.map +1 -0
  45. package/dist/src/circuit/transport/index.d.ts +33 -0
  46. package/dist/src/circuit/transport/index.d.ts.map +1 -0
  47. package/dist/src/circuit/transport/index.js +225 -0
  48. package/dist/src/circuit/transport/index.js.map +1 -0
  49. package/dist/src/circuit/transport/listener.d.ts +9 -0
  50. package/dist/src/circuit/transport/listener.d.ts.map +1 -0
  51. package/dist/src/circuit/transport/listener.js +76 -0
  52. package/dist/src/circuit/transport/listener.js.map +1 -0
  53. package/dist/src/circuit/transport/reservation-store.d.ts +60 -0
  54. package/dist/src/circuit/transport/reservation-store.d.ts.map +1 -0
  55. package/dist/src/circuit/transport/reservation-store.js +165 -0
  56. package/dist/src/circuit/transport/reservation-store.js.map +1 -0
  57. package/dist/src/circuit/utils.d.ts +4 -2
  58. package/dist/src/circuit/utils.d.ts.map +1 -1
  59. package/dist/src/circuit/utils.js +75 -102
  60. package/dist/src/circuit/utils.js.map +1 -1
  61. package/dist/src/components.d.ts +2 -1
  62. package/dist/src/components.d.ts.map +1 -1
  63. package/dist/src/components.js.map +1 -1
  64. package/dist/src/config.d.ts.map +1 -1
  65. package/dist/src/config.js +17 -20
  66. package/dist/src/config.js.map +1 -1
  67. package/dist/src/connection/index.d.ts +2 -2
  68. package/dist/src/connection/index.d.ts.map +1 -1
  69. package/dist/src/connection/index.js +1 -1
  70. package/dist/src/connection/index.js.map +1 -1
  71. package/dist/src/connection-manager/auto-dialler.d.ts +0 -4
  72. package/dist/src/connection-manager/auto-dialler.d.ts.map +1 -1
  73. package/dist/src/connection-manager/auto-dialler.js +0 -9
  74. package/dist/src/connection-manager/auto-dialler.js.map +1 -1
  75. package/dist/src/connection-manager/dialer/dial-request.js +1 -1
  76. package/dist/src/connection-manager/dialer/dial-request.js.map +1 -1
  77. package/dist/src/connection-manager/dialer/index.d.ts +2 -1
  78. package/dist/src/connection-manager/dialer/index.d.ts.map +1 -1
  79. package/dist/src/connection-manager/dialer/index.js +3 -3
  80. package/dist/src/connection-manager/dialer/index.js.map +1 -1
  81. package/dist/src/connection-manager/index.d.ts +0 -4
  82. package/dist/src/connection-manager/index.d.ts.map +1 -1
  83. package/dist/src/connection-manager/index.js +1 -1
  84. package/dist/src/connection-manager/index.js.map +1 -1
  85. package/dist/src/connection-manager/latency-monitor.d.ts.map +1 -1
  86. package/dist/src/connection-manager/latency-monitor.js +1 -1
  87. package/dist/src/connection-manager/latency-monitor.js.map +1 -1
  88. package/dist/src/connection-manager/visibility-change-emitter.d.ts.map +1 -1
  89. package/dist/src/content-routing/index.d.ts +6 -4
  90. package/dist/src/content-routing/index.d.ts.map +1 -1
  91. package/dist/src/content-routing/index.js +1 -1
  92. package/dist/src/content-routing/index.js.map +1 -1
  93. package/dist/src/content-routing/utils.d.ts +3 -3
  94. package/dist/src/content-routing/utils.d.ts.map +1 -1
  95. package/dist/src/dht/dht-content-routing.d.ts +2 -1
  96. package/dist/src/dht/dht-content-routing.d.ts.map +1 -1
  97. package/dist/src/dht/dht-content-routing.js.map +1 -1
  98. package/dist/src/dht/dht-peer-routing.d.ts +3 -2
  99. package/dist/src/dht/dht-peer-routing.d.ts.map +1 -1
  100. package/dist/src/dht/dht-peer-routing.js.map +1 -1
  101. package/dist/src/dht/dummy-dht.d.ts +1 -1
  102. package/dist/src/dht/dummy-dht.d.ts.map +1 -1
  103. package/dist/src/fetch/index.d.ts.map +1 -1
  104. package/dist/src/identify/index.d.ts +3 -1
  105. package/dist/src/identify/index.d.ts.map +1 -1
  106. package/dist/src/identify/index.js +12 -7
  107. package/dist/src/identify/index.js.map +1 -1
  108. package/dist/src/index.d.ts +13 -7
  109. package/dist/src/index.d.ts.map +1 -1
  110. package/dist/src/index.js.map +1 -1
  111. package/dist/src/insecure/index.d.ts.map +1 -1
  112. package/dist/src/insecure/index.js.map +1 -1
  113. package/dist/src/libp2p.d.ts +6 -4
  114. package/dist/src/libp2p.d.ts.map +1 -1
  115. package/dist/src/libp2p.js +19 -28
  116. package/dist/src/libp2p.js.map +1 -1
  117. package/dist/src/nat-manager.d.ts.map +1 -1
  118. package/dist/src/nat-manager.js +3 -1
  119. package/dist/src/nat-manager.js.map +1 -1
  120. package/dist/src/peer-record-updater.d.ts.map +1 -1
  121. package/dist/src/peer-routing.d.ts.map +1 -1
  122. package/dist/src/ping/index.d.ts.map +1 -1
  123. package/dist/src/pnet/crypto.d.ts +1 -1
  124. package/dist/src/pnet/crypto.d.ts.map +1 -1
  125. package/dist/src/pnet/crypto.js +1 -1
  126. package/dist/src/pnet/crypto.js.map +1 -1
  127. package/dist/src/pnet/key-generator.d.ts.map +1 -1
  128. package/dist/src/registrar.d.ts.map +1 -1
  129. package/dist/src/transport-manager.d.ts.map +1 -1
  130. package/dist/src/transport-manager.js +1 -1
  131. package/dist/src/transport-manager.js.map +1 -1
  132. package/dist/src/upgrader.d.ts +2 -1
  133. package/dist/src/upgrader.d.ts.map +1 -1
  134. package/dist/src/upgrader.js +19 -14
  135. package/dist/src/upgrader.js.map +1 -1
  136. package/package.json +23 -18
  137. package/src/address-manager/index.ts +62 -35
  138. package/src/autonat/constants.ts +4 -0
  139. package/src/autonat/index.ts +567 -0
  140. package/src/autonat/pb/index.proto +35 -0
  141. package/src/autonat/pb/index.ts +320 -0
  142. package/src/circuit/constants.ts +40 -5
  143. package/src/circuit/index.ts +17 -67
  144. package/src/circuit/server/advert-service.ts +108 -0
  145. package/src/circuit/server/index.ts +421 -0
  146. package/src/circuit/server/reservation-store.ts +111 -0
  147. package/src/circuit/{reservation-voucher.ts → server/reservation-voucher.ts} +3 -3
  148. package/src/circuit/transport/discovery.ts +127 -0
  149. package/src/circuit/transport/index.ts +325 -0
  150. package/src/circuit/transport/listener.ts +99 -0
  151. package/src/circuit/transport/reservation-store.ts +249 -0
  152. package/src/circuit/utils.ts +78 -115
  153. package/src/components.ts +9 -8
  154. package/src/config.ts +17 -20
  155. package/src/connection/index.ts +7 -7
  156. package/src/connection-manager/auto-dialler.ts +4 -20
  157. package/src/connection-manager/dialer/dial-request.ts +1 -1
  158. package/src/connection-manager/dialer/index.ts +10 -9
  159. package/src/connection-manager/index.ts +14 -19
  160. package/src/connection-manager/latency-monitor.ts +9 -9
  161. package/src/connection-manager/visibility-change-emitter.ts +4 -4
  162. package/src/content-routing/index.ts +10 -8
  163. package/src/content-routing/utils.ts +3 -3
  164. package/src/dht/dht-content-routing.ts +3 -2
  165. package/src/dht/dht-peer-routing.ts +3 -2
  166. package/src/dht/dummy-dht.ts +1 -1
  167. package/src/fetch/index.ts +7 -7
  168. package/src/identify/index.ts +22 -17
  169. package/src/index.ts +14 -7
  170. package/src/insecure/index.ts +2 -1
  171. package/src/libp2p.ts +33 -41
  172. package/src/nat-manager.ts +9 -7
  173. package/src/peer-record-updater.ts +4 -4
  174. package/src/peer-routing.ts +4 -4
  175. package/src/ping/index.ts +4 -4
  176. package/src/pnet/crypto.ts +3 -3
  177. package/src/pnet/key-generator.ts +1 -1
  178. package/src/registrar.ts +8 -8
  179. package/src/transport-manager.ts +10 -10
  180. package/src/upgrader.ts +20 -19
  181. package/src/version.ts +1 -1
  182. package/dist/src/circuit/client.d.ts +0 -81
  183. package/dist/src/circuit/client.d.ts.map +0 -1
  184. package/dist/src/circuit/client.js +0 -285
  185. package/dist/src/circuit/client.js.map +0 -1
  186. package/dist/src/circuit/hop.d.ts +0 -23
  187. package/dist/src/circuit/hop.d.ts.map +0 -1
  188. package/dist/src/circuit/hop.js +0 -167
  189. package/dist/src/circuit/hop.js.map +0 -1
  190. package/dist/src/circuit/interfaces.d.ts +0 -27
  191. package/dist/src/circuit/interfaces.d.ts.map +0 -1
  192. package/dist/src/circuit/interfaces.js +0 -2
  193. package/dist/src/circuit/interfaces.js.map +0 -1
  194. package/dist/src/circuit/listener.d.ts +0 -9
  195. package/dist/src/circuit/listener.d.ts.map +0 -1
  196. package/dist/src/circuit/listener.js +0 -62
  197. package/dist/src/circuit/listener.js.map +0 -1
  198. package/dist/src/circuit/multicodec.d.ts +0 -3
  199. package/dist/src/circuit/multicodec.d.ts.map +0 -1
  200. package/dist/src/circuit/multicodec.js +0 -3
  201. package/dist/src/circuit/multicodec.js.map +0 -1
  202. package/dist/src/circuit/relay.d.ts +0 -34
  203. package/dist/src/circuit/relay.d.ts.map +0 -1
  204. package/dist/src/circuit/relay.js +0 -61
  205. package/dist/src/circuit/relay.js.map +0 -1
  206. package/dist/src/circuit/reservation-store.d.ts.map +0 -1
  207. package/dist/src/circuit/reservation-store.js +0 -59
  208. package/dist/src/circuit/reservation-store.js.map +0 -1
  209. package/dist/src/circuit/reservation-voucher.d.ts.map +0 -1
  210. package/dist/src/circuit/reservation-voucher.js.map +0 -1
  211. package/dist/src/circuit/stop.d.ts +0 -19
  212. package/dist/src/circuit/stop.d.ts.map +0 -1
  213. package/dist/src/circuit/stop.js +0 -67
  214. package/dist/src/circuit/stop.js.map +0 -1
  215. package/dist/src/circuit/transport.d.ts +0 -66
  216. package/dist/src/circuit/transport.d.ts.map +0 -1
  217. package/dist/src/circuit/transport.js +0 -238
  218. package/dist/src/circuit/transport.js.map +0 -1
  219. package/src/circuit/client.ts +0 -381
  220. package/src/circuit/hop.ts +0 -210
  221. package/src/circuit/interfaces.ts +0 -28
  222. package/src/circuit/listener.ts +0 -83
  223. package/src/circuit/multicodec.ts +0 -3
  224. package/src/circuit/relay.ts +0 -87
  225. package/src/circuit/reservation-store.ts +0 -106
  226. package/src/circuit/stop.ts +0 -92
  227. package/src/circuit/transport.ts +0 -313
  228. /package/dist/src/circuit/{reservation-voucher.d.ts → server/reservation-voucher.d.ts} +0 -0
@@ -0,0 +1,325 @@
1
+ import { StopMessage, HopMessage, Status } from '../pb/index.js'
2
+ import { logger } from '@libp2p/logger'
3
+ import createError from 'err-code'
4
+ import * as mafmt from '@multiformats/mafmt'
5
+ import { multiaddr } from '@multiformats/multiaddr'
6
+ import { codes } from '../../errors.js'
7
+ import { streamToMaConnection } from '@libp2p/utils/stream-to-ma-conn'
8
+ import { createListener } from './listener.js'
9
+ import { symbol, Upgrader } from '@libp2p/interface-transport'
10
+ import { peerIdFromBytes, peerIdFromString } from '@libp2p/peer-id'
11
+ import type { AbortOptions } from '@libp2p/interfaces'
12
+ import type { IncomingStreamData, Registrar } from '@libp2p/interface-registrar'
13
+ import type { Listener, Transport, CreateListenerOptions } from '@libp2p/interface-transport'
14
+ import type { Connection, Stream } from '@libp2p/interface-connection'
15
+ import type { ConnectionGater } from '@libp2p/interface-connection-gater'
16
+ import type { PeerId } from '@libp2p/interface-peer-id'
17
+ import type { Multiaddr } from '@multiformats/multiaddr'
18
+ import type { PeerStore } from '@libp2p/interface-peer-store'
19
+ import type { ConnectionManager } from '@libp2p/interface-connection-manager'
20
+ import type { AddressManager } from '@libp2p/interface-address-manager'
21
+ import { pbStream } from 'it-pb-stream'
22
+ import type { ContentRouting } from '@libp2p/interface-content-routing'
23
+ import { CIRCUIT_PROTO_CODE, RELAY_V2_HOP_CODEC, RELAY_V2_STOP_CODEC } from '../constants.js'
24
+ import { RelayStoreInit, ReservationStore } from './reservation-store.js'
25
+ import { RelayDiscovery, RelayDiscoveryComponents } from './discovery.js'
26
+
27
+ const log = logger('libp2p:circuit-relay:transport')
28
+
29
+ const isValidStop = (request: StopMessage): request is Required<StopMessage> => {
30
+ if (request.peer == null) {
31
+ return false
32
+ }
33
+
34
+ try {
35
+ request.peer.addrs.forEach(multiaddr)
36
+ } catch {
37
+ return false
38
+ }
39
+
40
+ return true
41
+ }
42
+
43
+ export interface CircuitRelayTransportComponents extends RelayDiscoveryComponents {
44
+ peerId: PeerId
45
+ peerStore: PeerStore
46
+ registrar: Registrar
47
+ connectionManager: ConnectionManager
48
+ upgrader: Upgrader
49
+ addressManager: AddressManager
50
+ contentRouting: ContentRouting
51
+ connectionGater: ConnectionGater
52
+ }
53
+
54
+ interface ConnectOptions {
55
+ stream: Stream
56
+ connection: Connection
57
+ destinationPeer: PeerId
58
+ destinationAddr: Multiaddr
59
+ relayAddr: Multiaddr
60
+ ma: Multiaddr
61
+ disconnectOnFailure: boolean
62
+ }
63
+
64
+ /**
65
+ * RelayConfig configures the circuit v2 relay transport.
66
+ */
67
+ export interface CircuitRelayTransportInit extends RelayStoreInit {
68
+ /**
69
+ * The number of peers running diable relays to search for and
70
+ * connect to. (default: 0)
71
+ */
72
+ discoverRelays?: number
73
+ }
74
+
75
+ class CircuitRelayTransport implements Transport {
76
+ private readonly discovery?: RelayDiscovery
77
+ private readonly registrar: Registrar
78
+ private readonly peerStore: PeerStore
79
+ private readonly connectionManager: ConnectionManager
80
+ private readonly peerId: PeerId
81
+ private readonly upgrader: Upgrader
82
+ private readonly addressManager: AddressManager
83
+ private readonly connectionGater: ConnectionGater
84
+ private readonly reservationStore: ReservationStore
85
+ private started: boolean
86
+
87
+ constructor (components: CircuitRelayTransportComponents, init: CircuitRelayTransportInit) {
88
+ this.registrar = components.registrar
89
+ this.peerStore = components.peerStore
90
+ this.connectionManager = components.connectionManager
91
+ this.peerId = components.peerId
92
+ this.upgrader = components.upgrader
93
+ this.addressManager = components.addressManager
94
+ this.connectionGater = components.connectionGater
95
+
96
+ if (init.discoverRelays != null && init.discoverRelays > 0) {
97
+ this.discovery = new RelayDiscovery(components)
98
+ this.discovery.addEventListener('relay:discover', (evt) => {
99
+ this.reservationStore.addRelay(evt.detail, 'discovered')
100
+ .catch(err => {
101
+ log.error('could not add discovered relay %p', evt.detail, err)
102
+ })
103
+ })
104
+ }
105
+
106
+ this.reservationStore = new ReservationStore(components, init)
107
+ this.reservationStore.addEventListener('relay:not-enough-relays', () => {
108
+ this.discovery?.discover()
109
+ .catch(err => {
110
+ log.error('could not discover relays', err)
111
+ })
112
+ })
113
+
114
+ this.started = false
115
+ }
116
+
117
+ isStarted (): boolean {
118
+ return this.started
119
+ }
120
+
121
+ async start (): Promise<void> {
122
+ await this.reservationStore.start()
123
+ await this.discovery?.start()
124
+
125
+ await this.registrar.handle(RELAY_V2_STOP_CODEC, (data) => {
126
+ void this.onStop(data).catch(err => {
127
+ log.error(err)
128
+ })
129
+ })
130
+
131
+ this.started = true
132
+ }
133
+
134
+ async stop (): Promise<void> {
135
+ this.discovery?.stop()
136
+ await this.reservationStore.stop()
137
+ await this.registrar.unhandle(RELAY_V2_STOP_CODEC)
138
+
139
+ this.started = false
140
+ }
141
+
142
+ get [symbol] (): true {
143
+ return true
144
+ }
145
+
146
+ get [Symbol.toStringTag] (): 'libp2p/circuit-relay-v2' {
147
+ return 'libp2p/circuit-relay-v2'
148
+ }
149
+
150
+ /**
151
+ * Dial a peer over a relay
152
+ */
153
+ async dial (ma: Multiaddr, options: AbortOptions = {}): Promise<Connection> {
154
+ if (ma.protoCodes().filter(code => code === CIRCUIT_PROTO_CODE).length !== 1) {
155
+ const errMsg = 'Invalid circuit relay address'
156
+ log.error(errMsg, ma)
157
+ throw createError(new Error(errMsg), codes.ERR_RELAYED_DIAL)
158
+ }
159
+
160
+ // Check the multiaddr to see if it contains a relay and a destination peer
161
+ const addrs = ma.toString().split('/p2p-circuit')
162
+ const relayAddr = multiaddr(addrs[0])
163
+ const destinationAddr = multiaddr(addrs[addrs.length - 1])
164
+ const relayId = relayAddr.getPeerId()
165
+ const destinationId = destinationAddr.getPeerId()
166
+
167
+ if (relayId == null || destinationId == null) {
168
+ const errMsg = 'Circuit relay dial failed as addresses did not have peer id'
169
+ log.error(errMsg)
170
+ throw createError(new Error(errMsg), codes.ERR_RELAYED_DIAL)
171
+ }
172
+
173
+ const relayPeer = peerIdFromString(relayId)
174
+ const destinationPeer = peerIdFromString(destinationId)
175
+
176
+ let disconnectOnFailure = false
177
+ const relayConnections = this.connectionManager.getConnections(relayPeer)
178
+ let relayConnection = relayConnections[0]
179
+
180
+ if (relayConnection == null) {
181
+ await this.peerStore.addressBook.add(relayPeer, [relayAddr])
182
+ relayConnection = await this.connectionManager.openConnection(relayPeer, options)
183
+ disconnectOnFailure = true
184
+ }
185
+
186
+ try {
187
+ const stream = await relayConnection.newStream([RELAY_V2_HOP_CODEC])
188
+
189
+ return await this.connectV2({
190
+ stream,
191
+ connection: relayConnection,
192
+ destinationPeer,
193
+ destinationAddr,
194
+ relayAddr,
195
+ ma,
196
+ disconnectOnFailure
197
+ })
198
+ } catch (err: any) {
199
+ log.error('Circuit relay dial failed', err)
200
+ disconnectOnFailure && await relayConnection.close()
201
+ throw err
202
+ }
203
+ }
204
+
205
+ async connectV2 (
206
+ {
207
+ stream, connection, destinationPeer,
208
+ destinationAddr, relayAddr, ma,
209
+ disconnectOnFailure
210
+ }: ConnectOptions
211
+ ): Promise<Connection> {
212
+ try {
213
+ const pbstr = pbStream(stream)
214
+ const hopstr = pbstr.pb(HopMessage)
215
+ hopstr.write({
216
+ type: HopMessage.Type.CONNECT,
217
+ peer: {
218
+ id: destinationPeer.toBytes(),
219
+ addrs: [multiaddr(destinationAddr).bytes]
220
+ }
221
+ })
222
+
223
+ const status = await hopstr.read()
224
+
225
+ if (status.status !== Status.OK) {
226
+ throw createError(new Error(`failed to connect via relay with status ${status?.status?.toString() ?? 'undefined'}`), codes.ERR_HOP_REQUEST_FAILED)
227
+ }
228
+
229
+ // TODO: do something with limit and transient connection
230
+
231
+ const maConn = streamToMaConnection({
232
+ stream: pbstr.unwrap(),
233
+ remoteAddr: ma,
234
+ localAddr: relayAddr.encapsulate(`/p2p-circuit/p2p/${this.peerId.toString()}`)
235
+ })
236
+
237
+ log('new outbound connection %s', maConn.remoteAddr)
238
+ return await this.upgrader.upgradeOutbound(maConn)
239
+ } catch (err) {
240
+ log.error('Circuit relay dial failed', err)
241
+ disconnectOnFailure && await connection.close()
242
+ throw err
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Create a listener
248
+ */
249
+ createListener (options: CreateListenerOptions): Listener {
250
+ return createListener({
251
+ connectionManager: this.connectionManager,
252
+ relayStore: this.reservationStore
253
+ })
254
+ }
255
+
256
+ /**
257
+ * Filter check for all Multiaddrs that this transport can dial on
258
+ *
259
+ * @param {Multiaddr[]} multiaddrs
260
+ * @returns {Multiaddr[]}
261
+ */
262
+ filter (multiaddrs: Multiaddr[]): Multiaddr[] {
263
+ multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
264
+
265
+ return multiaddrs.filter((ma) => {
266
+ return mafmt.Circuit.matches(ma)
267
+ })
268
+ }
269
+
270
+ /**
271
+ * An incoming STOP request means a remote peer wants to dial us via a relay
272
+ */
273
+ async onStop ({ connection, stream }: IncomingStreamData): Promise<void> {
274
+ const pbstr = pbStream(stream)
275
+ const request = await pbstr.readPB(StopMessage)
276
+ log('received circuit v2 stop protocol request from %s', connection.remotePeer)
277
+
278
+ if (request?.type === undefined) {
279
+ return
280
+ }
281
+
282
+ const stopstr = pbstr.pb(StopMessage)
283
+ log('new circuit relay v2 stop stream from %s', connection.remotePeer)
284
+
285
+ // Validate the STOP request has the required input
286
+ if (request.type !== StopMessage.Type.CONNECT) {
287
+ log.error('invalid stop connect request via peer %s', connection.remotePeer)
288
+ stopstr.write({ type: StopMessage.Type.STATUS, status: Status.UNEXPECTED_MESSAGE })
289
+ return
290
+ }
291
+
292
+ if (!isValidStop(request)) {
293
+ log.error('invalid stop connect request via peer %s', connection.remotePeer)
294
+ stopstr.write({ type: StopMessage.Type.STATUS, status: Status.MALFORMED_MESSAGE })
295
+ return
296
+ }
297
+
298
+ const remotePeerId = peerIdFromBytes(request.peer.id)
299
+
300
+ if ((await this.connectionGater.denyInboundRelayedConnection?.(connection.remotePeer, remotePeerId)) === true) {
301
+ stopstr.write({ type: StopMessage.Type.STATUS, status: Status.PERMISSION_DENIED })
302
+ return
303
+ }
304
+
305
+ stopstr.write({ type: StopMessage.Type.STATUS, status: Status.OK })
306
+
307
+ const remoteAddr = connection.remoteAddr.encapsulate(`/p2p-circuit/p2p/${remotePeerId.toString()}`)
308
+ const localAddr = this.addressManager.getAddresses()[0]
309
+ const maConn = streamToMaConnection({
310
+ stream: pbstr.unwrap(),
311
+ remoteAddr,
312
+ localAddr
313
+ })
314
+
315
+ log('new inbound connection %s', maConn.remoteAddr)
316
+ await this.upgrader.upgradeInbound(maConn)
317
+ log('%s connection %s upgraded', 'inbound', maConn.remoteAddr)
318
+ }
319
+ }
320
+
321
+ export function circuitRelayTransport (init: CircuitRelayTransportInit = {}): (components: CircuitRelayTransportComponents) => Transport {
322
+ return (components) => {
323
+ return new CircuitRelayTransport(components, init)
324
+ }
325
+ }
@@ -0,0 +1,99 @@
1
+ import { CustomEvent, EventEmitter } from '@libp2p/interfaces/events'
2
+ import type { ConnectionManager } from '@libp2p/interface-connection-manager'
3
+ import type { Listener, ListenerEvents } from '@libp2p/interface-transport'
4
+ import type { Multiaddr } from '@multiformats/multiaddr'
5
+ import { multiaddr } from '@multiformats/multiaddr'
6
+ import type { ReservationStore } from './reservation-store'
7
+ import type { PeerId } from '@libp2p/interface-peer-id'
8
+ import { PeerMap } from '@libp2p/peer-collections'
9
+ import { logger } from '@libp2p/logger'
10
+
11
+ const log = logger('libp2p:circuit-relay:transport:listener')
12
+
13
+ export interface CircuitRelayTransportListenerComponents {
14
+ connectionManager: ConnectionManager
15
+ relayStore: ReservationStore
16
+ }
17
+
18
+ class CircuitRelayTransportListener extends EventEmitter<ListenerEvents> implements Listener {
19
+ private readonly connectionManager: ConnectionManager
20
+ private readonly relayStore: ReservationStore
21
+ private readonly listeningAddrs: PeerMap<Multiaddr>
22
+
23
+ constructor (components: CircuitRelayTransportListenerComponents) {
24
+ super()
25
+
26
+ this.connectionManager = components.connectionManager
27
+ this.relayStore = components.relayStore
28
+ this.listeningAddrs = new PeerMap()
29
+
30
+ // remove listening addrs when a relay is removed
31
+ this.relayStore.addEventListener('relay:removed', (evt) => {
32
+ this.#removeRelayPeer(evt.detail)
33
+ })
34
+
35
+ // remove listening addrs when a peer disconnects
36
+ this.connectionManager.addEventListener('peer:disconnect', (evt) => {
37
+ this.#removeRelayPeer(evt.detail.remotePeer)
38
+ })
39
+ }
40
+
41
+ async listen (addr: Multiaddr): Promise<void> {
42
+ log('listen on %s', addr)
43
+
44
+ const addrString = addr.toString().split('/p2p-circuit').find(a => a !== '')
45
+ const ma = multiaddr(addrString)
46
+ const relayConn = await this.connectionManager.openConnection(ma)
47
+
48
+ if (!this.relayStore.hasReservation(relayConn.remotePeer)) {
49
+ // addRelay calls transportManager.listen which calls this listen method
50
+ await this.relayStore.addRelay(relayConn.remotePeer, 'configured')
51
+ return
52
+ }
53
+
54
+ if (this.listeningAddrs.has(relayConn.remotePeer)) {
55
+ log('already listening on relay %p', relayConn.remotePeer)
56
+ return
57
+ }
58
+
59
+ this.listeningAddrs.set(relayConn.remotePeer, addr)
60
+ this.dispatchEvent(new CustomEvent('listening'))
61
+ }
62
+
63
+ /**
64
+ * Get fixed up multiaddrs
65
+ *
66
+ * NOTE: This method will grab the peers multiaddrs and expand them such that:
67
+ *
68
+ * a) If it's an existing /p2p-circuit address for a specific relay i.e.
69
+ * `/ip4/0.0.0.0/tcp/0/ipfs/QmRelay/p2p-circuit` this method will expand the
70
+ * address to `/ip4/0.0.0.0/tcp/0/ipfs/QmRelay/p2p-circuit/ipfs/QmPeer` where
71
+ * `QmPeer` is this peers id
72
+ * b) If it's not a /p2p-circuit address, it will encapsulate the address as a /p2p-circuit
73
+ * addr, such when dialing over a relay with this address, it will create the circuit using
74
+ * the encapsulated transport address. This is useful when for example, a peer should only
75
+ * be dialed over TCP rather than any other transport
76
+ */
77
+ getAddrs (): Multiaddr[] {
78
+ return [...this.listeningAddrs.values()]
79
+ }
80
+
81
+ async close (): Promise<void> {
82
+
83
+ }
84
+
85
+ #removeRelayPeer (peerId: PeerId): void {
86
+ const had = this.listeningAddrs.has(peerId)
87
+
88
+ this.listeningAddrs.delete(peerId)
89
+
90
+ if (had) {
91
+ // Announce listen addresses change
92
+ this.dispatchEvent(new CustomEvent('close'))
93
+ }
94
+ }
95
+ }
96
+
97
+ export function createListener (options: CircuitRelayTransportListenerComponents): Listener {
98
+ return new CircuitRelayTransportListener(options)
99
+ }
@@ -0,0 +1,249 @@
1
+ import type { PeerId } from '@libp2p/interface-peer-id'
2
+ import { PeerMap } from '@libp2p/peer-collections'
3
+ import PQueue from 'p-queue'
4
+ import { DEFAULT_RESERVATION_CONCURRENCY, RELAY_TAG, RELAY_V2_HOP_CODEC } from '../constants.js'
5
+ import { logger } from '@libp2p/logger'
6
+ import { pbStream } from 'it-pb-stream'
7
+ import type { ConnectionManager } from '@libp2p/interface-connection-manager'
8
+ import type { Connection } from '@libp2p/interface-connection'
9
+ import type { Reservation } from '../pb/index.js'
10
+ import { HopMessage, Status } from '../pb/index.js'
11
+ import { getExpiration } from '../utils.js'
12
+ import type { TransportManager } from '@libp2p/interface-transport'
13
+ import type { Startable } from '@libp2p/interfaces/dist/src/startable.js'
14
+ import { CustomEvent, EventEmitter } from '@libp2p/interfaces/events'
15
+ import type { PeerStore } from '@libp2p/interface-peer-store'
16
+ import { multiaddr } from '@multiformats/multiaddr'
17
+
18
+ const log = logger('libp2p:circuit-relay:transport:reservation-store')
19
+
20
+ // allow refreshing a relay reservation if it will expire in the next 10 minutes
21
+ const REFRESH_WINDOW = (60 * 1000) * 10
22
+
23
+ // try to refresh relay reservations 5 minutes before expiry
24
+ const REFRESH_TIMEOUT = (60 * 1000) * 5
25
+
26
+ export interface RelayStoreComponents {
27
+ peerId: PeerId
28
+ connectionManager: ConnectionManager
29
+ transportManager: TransportManager
30
+ peerStore: PeerStore
31
+ }
32
+
33
+ export interface RelayStoreInit {
34
+ /**
35
+ * Multiple relays may be discovered simultaneously - to prevent listening
36
+ * on too many relays, this value controls how many to attempt to reserve a
37
+ * slot on at once. If set to more than one, we may end up listening on
38
+ * more relays than the `maxReservations` value, but on networks with poor
39
+ * connectivity the user may wish to attempt to reserve on multiple relays
40
+ * simultaneously. (default: 1)
41
+ */
42
+ reservationConcurrency?: number
43
+
44
+ /**
45
+ * How many discovered relays to allow in the reservation store
46
+ */
47
+ discoverRelays?: number
48
+ }
49
+
50
+ export type RelayType = 'discovered' | 'configured'
51
+
52
+ interface RelayEntry {
53
+ timeout: ReturnType<typeof setTimeout>
54
+ type: RelayType
55
+ reservation: Reservation
56
+ }
57
+
58
+ export interface ReservationStoreEvents {
59
+ 'relay:not-enough-relays': CustomEvent
60
+ 'relay:removed': CustomEvent<PeerId>
61
+ }
62
+
63
+ export class ReservationStore extends EventEmitter<ReservationStoreEvents> implements Startable {
64
+ private readonly peerId: PeerId
65
+ private readonly connectionManager: ConnectionManager
66
+ private readonly transportManager: TransportManager
67
+ private readonly peerStore: PeerStore
68
+ private readonly reserveQueue: PQueue
69
+ private readonly reservations: PeerMap<RelayEntry>
70
+ private readonly maxDiscoveredRelays: number
71
+ private started: boolean
72
+
73
+ constructor (components: RelayStoreComponents, init?: RelayStoreInit) {
74
+ super()
75
+
76
+ this.peerId = components.peerId
77
+ this.connectionManager = components.connectionManager
78
+ this.transportManager = components.transportManager
79
+ this.peerStore = components.peerStore
80
+ this.reservations = new PeerMap()
81
+ this.maxDiscoveredRelays = init?.discoverRelays ?? 0
82
+ this.started = false
83
+
84
+ // ensure we don't listen on multiple relays simultaneously
85
+ this.reserveQueue = new PQueue({
86
+ concurrency: init?.reservationConcurrency ?? DEFAULT_RESERVATION_CONCURRENCY
87
+ })
88
+
89
+ // When a peer disconnects, if we had a reservation on that peer
90
+ // remove the reservation and multiaddr and maybe trigger search
91
+ // for new relays
92
+ this.connectionManager.addEventListener('peer:disconnect', (evt) => {
93
+ this.removeRelay(evt.detail.remotePeer)
94
+ })
95
+ }
96
+
97
+ isStarted (): boolean {
98
+ return this.started
99
+ }
100
+
101
+ async start (): Promise<void> {
102
+ this.started = true
103
+ }
104
+
105
+ async stop (): Promise<void> {
106
+ this.reservations.forEach(({ timeout }) => {
107
+ clearTimeout(timeout)
108
+ })
109
+ this.reservations.clear()
110
+
111
+ this.started = true
112
+ }
113
+
114
+ /**
115
+ * If the number of current relays is beneath the configured `maxReservations`
116
+ * value, and the passed peer id is not our own, and we have a non-relayed connection
117
+ * to the remote, and the remote peer speaks the hop protocol, try to reserve a slot
118
+ * on the remote peer
119
+ */
120
+ async addRelay (peerId: PeerId, type: RelayType): Promise<void> {
121
+ log('add relay', this.reserveQueue.size)
122
+
123
+ await this.reserveQueue.add(async () => {
124
+ try {
125
+ if (this.peerId.equals(peerId)) {
126
+ log('not trying to use self as relay')
127
+ return
128
+ }
129
+
130
+ // allow refresh of an existing reservation if it is about to expire
131
+ const existingReservation = this.reservations.get(peerId)
132
+
133
+ if (existingReservation != null) {
134
+ if (getExpiration(existingReservation.reservation.expire) > REFRESH_WINDOW) {
135
+ log('already have reservation on relay peer %p and it expires in more than 10 minutes', peerId)
136
+ return
137
+ }
138
+
139
+ clearTimeout(existingReservation.timeout)
140
+ this.reservations.delete(peerId)
141
+ }
142
+
143
+ if (type === 'discovered' && [...this.reservations.values()].reduce((acc, curr) => {
144
+ if (curr.type === 'discovered') {
145
+ acc++
146
+ }
147
+
148
+ return acc
149
+ }, 0) >= this.maxDiscoveredRelays) {
150
+ log('already have enough discovered relays')
151
+ return
152
+ }
153
+
154
+ const connection = await this.connectionManager.openConnection(peerId)
155
+
156
+ if (connection.remoteAddr.protoNames().includes('p2p-circuit')) {
157
+ log('not creating reservation over relayed connection')
158
+ return
159
+ }
160
+
161
+ const reservation = await this.#createReservation(connection)
162
+
163
+ log('created reservation on relay peer %p', peerId)
164
+
165
+ const expiration = getExpiration(reservation.expire)
166
+
167
+ const timeout = setTimeout(() => {
168
+ this.addRelay(peerId, type).catch(err => {
169
+ log.error('could not refresh reservation to relay %p', peerId, err)
170
+ })
171
+ }, Math.max(expiration - REFRESH_TIMEOUT, 0))
172
+
173
+ this.reservations.set(peerId, {
174
+ timeout,
175
+ reservation,
176
+ type
177
+ })
178
+
179
+ // ensure we don't close the connection to the relay
180
+ await this.peerStore.tagPeer(peerId, RELAY_TAG, {
181
+ value: 1,
182
+ ttl: expiration
183
+ })
184
+
185
+ await this.transportManager.listen(
186
+ reservation.addrs.map(ma => {
187
+ return multiaddr(ma).encapsulate('/p2p-circuit')
188
+ })
189
+ )
190
+ } catch (err) {
191
+ log.error('could not reserve slot on %p', peerId, err)
192
+ }
193
+ })
194
+ }
195
+
196
+ hasReservation (peerId: PeerId): boolean {
197
+ return this.reservations.has(peerId)
198
+ }
199
+
200
+ async #createReservation (connection: Connection): Promise<Reservation> {
201
+ log('requesting reservation from %s', connection.remotePeer)
202
+ const stream = await connection.newStream(RELAY_V2_HOP_CODEC)
203
+ const pbstr = pbStream(stream)
204
+ const hopstr = pbstr.pb(HopMessage)
205
+ hopstr.write({ type: HopMessage.Type.RESERVE })
206
+
207
+ let response: HopMessage
208
+
209
+ try {
210
+ response = await hopstr.read()
211
+ } catch (err: any) {
212
+ log.error('error parsing reserve message response from %s because', connection.remotePeer, err.message)
213
+ throw err
214
+ } finally {
215
+ stream.close()
216
+ }
217
+
218
+ if (response.status === Status.OK && (response.reservation != null)) {
219
+ return response.reservation
220
+ }
221
+
222
+ const errMsg = `reservation failed with status ${response.status ?? 'undefined'}`
223
+ log.error(errMsg)
224
+
225
+ throw new Error(errMsg)
226
+ }
227
+
228
+ /**
229
+ * Remove listen relay
230
+ */
231
+ removeRelay (peerId: PeerId): void {
232
+ const existingReservation = this.reservations.get(peerId)
233
+
234
+ if (existingReservation == null) {
235
+ return
236
+ }
237
+
238
+ log('removing relay %p', peerId)
239
+
240
+ clearTimeout(existingReservation.timeout)
241
+ this.reservations.delete(peerId)
242
+
243
+ this.safeDispatchEvent('relay:removed', { detail: peerId })
244
+
245
+ if (this.reservations.size < this.maxDiscoveredRelays) {
246
+ this.dispatchEvent(new CustomEvent('relay:not-enough-relays'))
247
+ }
248
+ }
249
+ }