libp2p 0.42.2 → 0.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. package/README.md +4 -4
  2. package/dist/index.min.js +29 -29
  3. package/dist/src/address-manager/index.d.ts +1 -10
  4. package/dist/src/address-manager/index.d.ts.map +1 -1
  5. package/dist/src/address-manager/index.js +46 -29
  6. package/dist/src/address-manager/index.js.map +1 -1
  7. package/dist/src/autonat/constants.d.ts +4 -0
  8. package/dist/src/autonat/constants.d.ts.map +1 -0
  9. package/dist/src/autonat/constants.js +4 -0
  10. package/dist/src/autonat/constants.js.map +1 -0
  11. package/dist/src/autonat/index.d.ts +64 -0
  12. package/dist/src/autonat/index.d.ts.map +1 -0
  13. package/dist/src/autonat/index.js +417 -0
  14. package/dist/src/autonat/index.js.map +1 -0
  15. package/dist/src/autonat/pb/index.d.ts +57 -0
  16. package/dist/src/autonat/pb/index.d.ts.map +1 -0
  17. package/dist/src/autonat/pb/index.js +250 -0
  18. package/dist/src/autonat/pb/index.js.map +1 -0
  19. package/dist/src/circuit/constants.d.ts +31 -6
  20. package/dist/src/circuit/constants.d.ts.map +1 -1
  21. package/dist/src/circuit/constants.js +37 -7
  22. package/dist/src/circuit/constants.js.map +1 -1
  23. package/dist/src/circuit/index.d.ts +16 -61
  24. package/dist/src/circuit/index.d.ts.map +1 -1
  25. package/dist/src/circuit/index.js +2 -68
  26. package/dist/src/circuit/index.js.map +1 -1
  27. package/dist/src/circuit/pb/index.d.ts +83 -43
  28. package/dist/src/circuit/pb/index.d.ts.map +1 -1
  29. package/dist/src/circuit/pb/index.js +363 -127
  30. package/dist/src/circuit/pb/index.js.map +1 -1
  31. package/dist/src/circuit/server/advert-service.d.ts +43 -0
  32. package/dist/src/circuit/server/advert-service.d.ts.map +1 -0
  33. package/dist/src/circuit/server/advert-service.js +69 -0
  34. package/dist/src/circuit/server/advert-service.js.map +1 -0
  35. package/dist/src/circuit/server/index.d.ts +60 -0
  36. package/dist/src/circuit/server/index.d.ts.map +1 -0
  37. package/dist/src/circuit/server/index.js +287 -0
  38. package/dist/src/circuit/server/index.js.map +1 -0
  39. package/dist/src/circuit/server/reservation-store.d.ts +49 -0
  40. package/dist/src/circuit/server/reservation-store.d.ts.map +1 -0
  41. package/dist/src/circuit/server/reservation-store.js +57 -0
  42. package/dist/src/circuit/server/reservation-store.js.map +1 -0
  43. package/dist/src/circuit/server/reservation-voucher.d.ts +18 -0
  44. package/dist/src/circuit/server/reservation-voucher.d.ts.map +1 -0
  45. package/dist/src/circuit/server/reservation-voucher.js +33 -0
  46. package/dist/src/circuit/server/reservation-voucher.js.map +1 -0
  47. package/dist/src/circuit/transport/discovery.d.ts +45 -0
  48. package/dist/src/circuit/transport/discovery.d.ts.map +1 -0
  49. package/dist/src/circuit/transport/discovery.js +82 -0
  50. package/dist/src/circuit/transport/discovery.js.map +1 -0
  51. package/dist/src/circuit/transport/index.d.ts +33 -0
  52. package/dist/src/circuit/transport/index.d.ts.map +1 -0
  53. package/dist/src/circuit/transport/index.js +225 -0
  54. package/dist/src/circuit/transport/index.js.map +1 -0
  55. package/dist/src/circuit/transport/listener.d.ts +9 -0
  56. package/dist/src/circuit/transport/listener.d.ts.map +1 -0
  57. package/dist/src/circuit/transport/listener.js +76 -0
  58. package/dist/src/circuit/transport/listener.js.map +1 -0
  59. package/dist/src/circuit/transport/reservation-store.d.ts +60 -0
  60. package/dist/src/circuit/transport/reservation-store.d.ts.map +1 -0
  61. package/dist/src/circuit/transport/reservation-store.js +165 -0
  62. package/dist/src/circuit/transport/reservation-store.js.map +1 -0
  63. package/dist/src/circuit/utils.d.ts +7 -0
  64. package/dist/src/circuit/utils.d.ts.map +1 -1
  65. package/dist/src/circuit/utils.js +87 -0
  66. package/dist/src/circuit/utils.js.map +1 -1
  67. package/dist/src/components.d.ts +2 -1
  68. package/dist/src/components.d.ts.map +1 -1
  69. package/dist/src/components.js +13 -13
  70. package/dist/src/components.js.map +1 -1
  71. package/dist/src/config.d.ts.map +1 -1
  72. package/dist/src/config.js +21 -25
  73. package/dist/src/config.js.map +1 -1
  74. package/dist/src/connection/index.d.ts +2 -2
  75. package/dist/src/connection/index.d.ts.map +1 -1
  76. package/dist/src/connection/index.js +4 -4
  77. package/dist/src/connection/index.js.map +1 -1
  78. package/dist/src/connection-manager/auto-dialler.d.ts +0 -4
  79. package/dist/src/connection-manager/auto-dialler.d.ts.map +1 -1
  80. package/dist/src/connection-manager/auto-dialler.js +18 -18
  81. package/dist/src/connection-manager/auto-dialler.js.map +1 -1
  82. package/dist/src/connection-manager/dialer/dial-request.d.ts.map +1 -1
  83. package/dist/src/connection-manager/dialer/dial-request.js +13 -6
  84. package/dist/src/connection-manager/dialer/dial-request.js.map +1 -1
  85. package/dist/src/connection-manager/dialer/index.d.ts +10 -1
  86. package/dist/src/connection-manager/dialer/index.d.ts.map +1 -1
  87. package/dist/src/connection-manager/dialer/index.js +24 -9
  88. package/dist/src/connection-manager/dialer/index.js.map +1 -1
  89. package/dist/src/connection-manager/index.d.ts +1 -4
  90. package/dist/src/connection-manager/index.d.ts.map +1 -1
  91. package/dist/src/connection-manager/index.js +8 -5
  92. package/dist/src/connection-manager/index.js.map +1 -1
  93. package/dist/src/connection-manager/latency-monitor.d.ts.map +1 -1
  94. package/dist/src/connection-manager/latency-monitor.js +1 -1
  95. package/dist/src/connection-manager/latency-monitor.js.map +1 -1
  96. package/dist/src/connection-manager/visibility-change-emitter.d.ts.map +1 -1
  97. package/dist/src/content-routing/index.d.ts +6 -4
  98. package/dist/src/content-routing/index.d.ts.map +1 -1
  99. package/dist/src/content-routing/index.js +9 -9
  100. package/dist/src/content-routing/index.js.map +1 -1
  101. package/dist/src/content-routing/utils.d.ts +3 -3
  102. package/dist/src/content-routing/utils.d.ts.map +1 -1
  103. package/dist/src/content-routing/utils.js +2 -2
  104. package/dist/src/content-routing/utils.js.map +1 -1
  105. package/dist/src/dht/dht-content-routing.d.ts +2 -1
  106. package/dist/src/dht/dht-content-routing.d.ts.map +1 -1
  107. package/dist/src/dht/dht-content-routing.js +2 -2
  108. package/dist/src/dht/dht-content-routing.js.map +1 -1
  109. package/dist/src/dht/dht-peer-routing.d.ts +3 -2
  110. package/dist/src/dht/dht-peer-routing.d.ts.map +1 -1
  111. package/dist/src/dht/dht-peer-routing.js +2 -2
  112. package/dist/src/dht/dht-peer-routing.js.map +1 -1
  113. package/dist/src/dht/dummy-dht.d.ts +1 -1
  114. package/dist/src/dht/dummy-dht.d.ts.map +1 -1
  115. package/dist/src/dht/dummy-dht.js +12 -12
  116. package/dist/src/dht/dummy-dht.js.map +1 -1
  117. package/dist/src/errors.d.ts +2 -1
  118. package/dist/src/errors.d.ts.map +1 -1
  119. package/dist/src/errors.js +1 -0
  120. package/dist/src/errors.js.map +1 -1
  121. package/dist/src/fetch/index.d.ts.map +1 -1
  122. package/dist/src/fetch/index.js +6 -6
  123. package/dist/src/fetch/index.js.map +1 -1
  124. package/dist/src/fetch/pb/proto.d.ts +3 -3
  125. package/dist/src/fetch/pb/proto.d.ts.map +1 -1
  126. package/dist/src/fetch/pb/proto.js +4 -3
  127. package/dist/src/fetch/pb/proto.js.map +1 -1
  128. package/dist/src/get-peer.js +2 -2
  129. package/dist/src/get-peer.js.map +1 -1
  130. package/dist/src/identify/index.d.ts +3 -1
  131. package/dist/src/identify/index.d.ts.map +1 -1
  132. package/dist/src/identify/index.js +19 -14
  133. package/dist/src/identify/index.js.map +1 -1
  134. package/dist/src/identify/pb/message.d.ts +2 -2
  135. package/dist/src/identify/pb/message.d.ts.map +1 -1
  136. package/dist/src/identify/pb/message.js +1 -0
  137. package/dist/src/identify/pb/message.js.map +1 -1
  138. package/dist/src/index.d.ts +14 -8
  139. package/dist/src/index.d.ts.map +1 -1
  140. package/dist/src/index.js.map +1 -1
  141. package/dist/src/insecure/index.d.ts.map +1 -1
  142. package/dist/src/insecure/index.js.map +1 -1
  143. package/dist/src/insecure/pb/proto.d.ts +3 -3
  144. package/dist/src/insecure/pb/proto.d.ts.map +1 -1
  145. package/dist/src/insecure/pb/proto.js +4 -5
  146. package/dist/src/insecure/pb/proto.js.map +1 -1
  147. package/dist/src/libp2p.d.ts +7 -3
  148. package/dist/src/libp2p.d.ts.map +1 -1
  149. package/dist/src/libp2p.js +35 -35
  150. package/dist/src/libp2p.js.map +1 -1
  151. package/dist/src/nat-manager.d.ts.map +1 -1
  152. package/dist/src/nat-manager.js +5 -3
  153. package/dist/src/nat-manager.js.map +1 -1
  154. package/dist/src/peer-record-updater.d.ts.map +1 -1
  155. package/dist/src/peer-routing.d.ts.map +1 -1
  156. package/dist/src/peer-routing.js +5 -5
  157. package/dist/src/peer-routing.js.map +1 -1
  158. package/dist/src/ping/index.d.ts.map +1 -1
  159. package/dist/src/ping/index.js +2 -2
  160. package/dist/src/ping/index.js.map +1 -1
  161. package/dist/src/pnet/crypto.d.ts +1 -1
  162. package/dist/src/pnet/crypto.d.ts.map +1 -1
  163. package/dist/src/pnet/crypto.js +1 -1
  164. package/dist/src/pnet/crypto.js.map +1 -1
  165. package/dist/src/pnet/index.js +3 -3
  166. package/dist/src/pnet/index.js.map +1 -1
  167. package/dist/src/pnet/key-generator.d.ts.map +1 -1
  168. package/dist/src/pubsub/dummy-pubsub.js +9 -9
  169. package/dist/src/pubsub/dummy-pubsub.js.map +1 -1
  170. package/dist/src/registrar.d.ts.map +1 -1
  171. package/dist/src/registrar.js +4 -4
  172. package/dist/src/registrar.js.map +1 -1
  173. package/dist/src/transport-manager.d.ts.map +1 -1
  174. package/dist/src/transport-manager.js +8 -9
  175. package/dist/src/transport-manager.js.map +1 -1
  176. package/dist/src/upgrader.d.ts +2 -1
  177. package/dist/src/upgrader.d.ts.map +1 -1
  178. package/dist/src/upgrader.js +38 -33
  179. package/dist/src/upgrader.js.map +1 -1
  180. package/dist/typedoc-urls.json +19 -6
  181. package/package.json +36 -33
  182. package/src/address-manager/index.ts +62 -35
  183. package/src/autonat/constants.ts +4 -0
  184. package/src/autonat/index.ts +567 -0
  185. package/src/autonat/pb/index.proto +35 -0
  186. package/src/autonat/pb/index.ts +320 -0
  187. package/src/circuit/constants.ts +48 -7
  188. package/src/circuit/index.ts +17 -129
  189. package/src/circuit/pb/index.proto +57 -32
  190. package/src/circuit/pb/index.ts +448 -138
  191. package/src/circuit/server/advert-service.ts +108 -0
  192. package/src/circuit/server/index.ts +421 -0
  193. package/src/circuit/server/reservation-store.ts +111 -0
  194. package/src/circuit/server/reservation-voucher.ts +51 -0
  195. package/src/circuit/transport/discovery.ts +127 -0
  196. package/src/circuit/transport/index.ts +325 -0
  197. package/src/circuit/transport/listener.ts +99 -0
  198. package/src/circuit/transport/reservation-store.ts +249 -0
  199. package/src/circuit/utils.ts +108 -0
  200. package/src/components.ts +22 -21
  201. package/src/config.ts +21 -25
  202. package/src/connection/index.ts +10 -10
  203. package/src/connection-manager/auto-dialler.ts +36 -40
  204. package/src/connection-manager/dialer/dial-request.ts +13 -6
  205. package/src/connection-manager/dialer/index.ts +34 -15
  206. package/src/connection-manager/index.ts +22 -23
  207. package/src/connection-manager/latency-monitor.ts +9 -9
  208. package/src/connection-manager/visibility-change-emitter.ts +4 -4
  209. package/src/content-routing/index.ts +18 -16
  210. package/src/content-routing/utils.ts +5 -5
  211. package/src/dht/dht-content-routing.ts +5 -4
  212. package/src/dht/dht-peer-routing.ts +5 -4
  213. package/src/dht/dummy-dht.ts +13 -13
  214. package/src/errors.ts +2 -1
  215. package/src/fetch/index.ts +13 -13
  216. package/src/fetch/pb/proto.ts +8 -7
  217. package/src/get-peer.ts +3 -3
  218. package/src/identify/index.ts +29 -24
  219. package/src/identify/pb/message.ts +3 -2
  220. package/src/index.ts +15 -8
  221. package/src/insecure/index.ts +2 -1
  222. package/src/insecure/pb/proto.ts +8 -9
  223. package/src/libp2p.ts +52 -46
  224. package/src/nat-manager.ts +11 -9
  225. package/src/peer-record-updater.ts +4 -4
  226. package/src/peer-routing.ts +9 -9
  227. package/src/ping/index.ts +6 -6
  228. package/src/pnet/crypto.ts +3 -3
  229. package/src/pnet/index.ts +3 -3
  230. package/src/pnet/key-generator.ts +1 -1
  231. package/src/pubsub/dummy-pubsub.ts +9 -9
  232. package/src/registrar.ts +12 -12
  233. package/src/transport-manager.ts +17 -18
  234. package/src/upgrader.ts +43 -42
  235. package/src/version.ts +1 -1
  236. package/dist/src/circuit/auto-relay.d.ts +0 -46
  237. package/dist/src/circuit/auto-relay.d.ts.map +0 -1
  238. package/dist/src/circuit/auto-relay.js +0 -220
  239. package/dist/src/circuit/auto-relay.js.map +0 -1
  240. package/dist/src/circuit/circuit/hop.d.ts +0 -42
  241. package/dist/src/circuit/circuit/hop.d.ts.map +0 -1
  242. package/dist/src/circuit/circuit/hop.js +0 -142
  243. package/dist/src/circuit/circuit/hop.js.map +0 -1
  244. package/dist/src/circuit/circuit/stop.d.ts +0 -24
  245. package/dist/src/circuit/circuit/stop.d.ts.map +0 -1
  246. package/dist/src/circuit/circuit/stop.js +0 -51
  247. package/dist/src/circuit/circuit/stop.js.map +0 -1
  248. package/dist/src/circuit/circuit/stream-handler.d.ts +0 -40
  249. package/dist/src/circuit/circuit/stream-handler.d.ts.map +0 -1
  250. package/dist/src/circuit/circuit/stream-handler.js +0 -59
  251. package/dist/src/circuit/circuit/stream-handler.js.map +0 -1
  252. package/dist/src/circuit/circuit/utils.d.ts +0 -7
  253. package/dist/src/circuit/circuit/utils.d.ts.map +0 -1
  254. package/dist/src/circuit/circuit/utils.js +0 -43
  255. package/dist/src/circuit/circuit/utils.js.map +0 -1
  256. package/dist/src/circuit/listener.d.ts +0 -9
  257. package/dist/src/circuit/listener.d.ts.map +0 -1
  258. package/dist/src/circuit/listener.js +0 -62
  259. package/dist/src/circuit/listener.js.map +0 -1
  260. package/dist/src/circuit/multicodec.d.ts +0 -2
  261. package/dist/src/circuit/multicodec.d.ts.map +0 -1
  262. package/dist/src/circuit/multicodec.js +0 -2
  263. package/dist/src/circuit/multicodec.js.map +0 -1
  264. package/dist/src/circuit/transport.d.ts +0 -51
  265. package/dist/src/circuit/transport.d.ts.map +0 -1
  266. package/dist/src/circuit/transport.js +0 -223
  267. package/dist/src/circuit/transport.js.map +0 -1
  268. package/dist/src/keychain/cms.d.ts +0 -33
  269. package/dist/src/keychain/cms.d.ts.map +0 -1
  270. package/dist/src/keychain/cms.js +0 -129
  271. package/dist/src/keychain/cms.js.map +0 -1
  272. package/dist/src/keychain/index.d.ts +0 -151
  273. package/dist/src/keychain/index.d.ts.map +0 -1
  274. package/dist/src/keychain/index.js +0 -519
  275. package/dist/src/keychain/index.js.map +0 -1
  276. package/dist/src/keychain/util.d.ts +0 -21
  277. package/dist/src/keychain/util.d.ts.map +0 -1
  278. package/dist/src/keychain/util.js +0 -79
  279. package/dist/src/keychain/util.js.map +0 -1
  280. package/src/circuit/IMPLEMENTATION_NOTES.md +0 -128
  281. package/src/circuit/README.md +0 -111
  282. package/src/circuit/auto-relay.ts +0 -292
  283. package/src/circuit/circuit/hop.ts +0 -220
  284. package/src/circuit/circuit/stop.ts +0 -83
  285. package/src/circuit/circuit/stream-handler.ts +0 -87
  286. package/src/circuit/circuit/utils.ts +0 -44
  287. package/src/circuit/listener.ts +0 -83
  288. package/src/circuit/multicodec.ts +0 -2
  289. package/src/circuit/transport.ts +0 -278
  290. package/src/keychain/README.md +0 -55
  291. package/src/keychain/cms.ts +0 -150
  292. package/src/keychain/doc/private-key.png +0 -0
  293. package/src/keychain/doc/private-key.xml +0 -1
  294. package/src/keychain/index.ts +0 -629
  295. package/src/keychain/util.ts +0 -82
@@ -0,0 +1,108 @@
1
+ import { logger } from '@libp2p/logger'
2
+ import { codes } from '../../errors.js'
3
+ import { namespaceToCid } from '../utils.js'
4
+ import {
5
+ DEFAULT_ADVERT_BOOT_DELAY,
6
+ RELAY_RENDEZVOUS_NS
7
+ } from '../constants.js'
8
+ import type { Startable } from '@libp2p/interfaces/startable'
9
+ import type { ContentRouting } from '@libp2p/interface-content-routing'
10
+ import { CustomEvent, EventEmitter } from '@libp2p/interfaces/events'
11
+ import pRetry from 'p-retry'
12
+
13
+ const log = logger('libp2p:circuit-relay:advert-service')
14
+
15
+ export interface AdvertServiceInit {
16
+ /**
17
+ * How long to wait after startup to begin advertising the service
18
+ * - if some configured content routers take a while to warm up (for
19
+ * example, the DHT needs some peers to be able to publish) this
20
+ * value should be high enough that they will have warmed up
21
+ */
22
+ bootDelay?: number
23
+ }
24
+
25
+ export interface AdvertServiceComponents {
26
+ contentRouting: ContentRouting
27
+ }
28
+
29
+ export interface AdvertServiceEvents {
30
+ 'advert:success': CustomEvent<unknown>
31
+ 'advert:error': CustomEvent<Error>
32
+ }
33
+
34
+ export class AdvertService extends EventEmitter<AdvertServiceEvents> implements Startable {
35
+ private readonly contentRouting: ContentRouting
36
+ private timeout?: any
37
+ private started: boolean
38
+ private readonly bootDelay: number
39
+
40
+ /**
41
+ * Creates an instance of Relay
42
+ */
43
+ constructor (components: AdvertServiceComponents, init?: AdvertServiceInit) {
44
+ super()
45
+
46
+ this.contentRouting = components.contentRouting
47
+ this.bootDelay = init?.bootDelay ?? DEFAULT_ADVERT_BOOT_DELAY
48
+ this.started = false
49
+ }
50
+
51
+ isStarted (): boolean {
52
+ return this.started
53
+ }
54
+
55
+ /**
56
+ * Start Relay service
57
+ */
58
+ start (): void {
59
+ if (this.started) {
60
+ return
61
+ }
62
+
63
+ // Advertise service if HOP enabled and advertising enabled
64
+ this.timeout = setTimeout(() => {
65
+ this._advertiseService().catch(err => {
66
+ log.error('could not advertise service', err)
67
+ })
68
+ }, this.bootDelay)
69
+
70
+ this.started = true
71
+ }
72
+
73
+ /**
74
+ * Stop Relay service
75
+ */
76
+ stop (): void {
77
+ try {
78
+ clearTimeout(this.timeout)
79
+ } catch (err) { }
80
+
81
+ this.started = false
82
+ }
83
+
84
+ /**
85
+ * Advertise hop relay service in the network.
86
+ */
87
+ async _advertiseService (): Promise<void> {
88
+ await pRetry(async () => {
89
+ try {
90
+ const cid = await namespaceToCid(RELAY_RENDEZVOUS_NS)
91
+ await this.contentRouting.provide(cid)
92
+
93
+ this.dispatchEvent(new CustomEvent('advert:success'))
94
+ } catch (err: any) {
95
+ this.safeDispatchEvent('advert:error', { detail: err })
96
+
97
+ if (err.code === codes.ERR_NO_ROUTERS_AVAILABLE) {
98
+ log.error('a content router, such as a DHT, must be provided in order to advertise the relay service', err)
99
+ this.stop()
100
+ return
101
+ }
102
+
103
+ log.error('could not advertise service', err)
104
+ throw err
105
+ }
106
+ })
107
+ }
108
+ }
@@ -0,0 +1,421 @@
1
+ import { logger } from '@libp2p/logger'
2
+ import { createLimitedRelay } from '../utils.js'
3
+ import {
4
+ CIRCUIT_PROTO_CODE,
5
+ DEFAULT_HOP_TIMEOUT,
6
+ RELAY_SOURCE_TAG
7
+ , RELAY_V2_HOP_CODEC, RELAY_V2_STOP_CODEC
8
+ } from '../constants.js'
9
+ import type { PeerStore } from '@libp2p/interface-peer-store'
10
+ import type { Startable } from '@libp2p/interfaces/startable'
11
+ import { ReservationStore, ReservationStoreInit } from './reservation-store.js'
12
+ import type { IncomingStreamData, Registrar } from '@libp2p/interface-registrar'
13
+ import { AdvertService, AdvertServiceComponents, AdvertServiceInit } from './advert-service.js'
14
+ import pDefer from 'p-defer'
15
+ import { pbStream, ProtobufStream } from 'it-pb-stream'
16
+ import { HopMessage, Reservation, Status, StopMessage } from '../pb/index.js'
17
+ import { Multiaddr, multiaddr } from '@multiformats/multiaddr'
18
+ import type { Connection, Stream } from '@libp2p/interface-connection'
19
+ import type { ConnectionGater } from '@libp2p/interface-connection-gater'
20
+ import { peerIdFromBytes } from '@libp2p/peer-id'
21
+ import type { PeerId } from '@libp2p/interface-peer-id'
22
+ import { RecordEnvelope } from '@libp2p/peer-record'
23
+ import { ReservationVoucherRecord } from './reservation-voucher.js'
24
+ import type { AddressManager } from '@libp2p/interface-address-manager'
25
+ import type { ConnectionManager } from '@libp2p/interface-connection-manager'
26
+ import type { CircuitRelayService, RelayReservation } from '../index.js'
27
+ import { CustomEvent, EventEmitter } from '@libp2p/interfaces/events'
28
+ import { setMaxListeners } from 'events'
29
+ import type { PeerMap } from '@libp2p/peer-collections'
30
+
31
+ const log = logger('libp2p:circuit-relay:server')
32
+
33
+ const isRelayAddr = (ma: Multiaddr): boolean => ma.protoCodes().includes(CIRCUIT_PROTO_CODE)
34
+
35
+ export interface CircuitRelayServerInit {
36
+ /**
37
+ * Incoming hop requests must complete within this time in ms otherwise
38
+ * the stream will be reset (default: 30s)
39
+ */
40
+ hopTimeout?: number
41
+
42
+ /**
43
+ * If true, advertise this service via libp2p content routing to allow
44
+ * peers to locate us on the network (default: false)
45
+ */
46
+ advertise?: boolean | AdvertServiceInit
47
+
48
+ /**
49
+ * Configuration of reservations
50
+ */
51
+ reservations?: ReservationStoreInit
52
+
53
+ /**
54
+ * The maximum number of simultaneous HOP inbound streams that can be open at once
55
+ */
56
+ maxInboundHopStreams?: number
57
+
58
+ /**
59
+ * The maximum number of simultaneous HOP outbound streams that can be open at once
60
+ */
61
+ maxOutboundHopStreams?: number
62
+ }
63
+
64
+ export interface HopProtocolOptions {
65
+ connection: Connection
66
+ request: HopMessage
67
+ stream: ProtobufStream<Stream>
68
+ }
69
+
70
+ export interface StopOptions {
71
+ connection: Connection
72
+ request: StopMessage
73
+ }
74
+
75
+ export interface CircuitRelayServerComponents extends AdvertServiceComponents {
76
+ registrar: Registrar
77
+ peerStore: PeerStore
78
+ addressManager: AddressManager
79
+ peerId: PeerId
80
+ connectionManager: ConnectionManager
81
+ connectionGater: ConnectionGater
82
+ }
83
+
84
+ export interface RelayServerEvents {
85
+ 'relay:reservation': CustomEvent<RelayReservation>
86
+ 'relay:advert:success': CustomEvent<unknown>
87
+ 'relay:advert:error': CustomEvent<Error>
88
+ }
89
+
90
+ class CircuitRelayServer extends EventEmitter<RelayServerEvents> implements Startable, CircuitRelayService {
91
+ private readonly registrar: Registrar
92
+ private readonly peerStore: PeerStore
93
+ private readonly addressManager: AddressManager
94
+ private readonly peerId: PeerId
95
+ private readonly connectionManager: ConnectionManager
96
+ private readonly connectionGater: ConnectionGater
97
+ private readonly reservationStore: ReservationStore
98
+ private readonly advertService: AdvertService | undefined
99
+ private started: boolean
100
+ private readonly hopTimeout: number
101
+ private readonly shutdownController: AbortController
102
+ private readonly maxInboundHopStreams?: number
103
+ private readonly maxOutboundHopStreams?: number
104
+
105
+ /**
106
+ * Creates an instance of Relay
107
+ */
108
+ constructor (components: CircuitRelayServerComponents, init: CircuitRelayServerInit = {}) {
109
+ super()
110
+
111
+ this.registrar = components.registrar
112
+ this.peerStore = components.peerStore
113
+ this.addressManager = components.addressManager
114
+ this.peerId = components.peerId
115
+ this.connectionManager = components.connectionManager
116
+ this.connectionGater = components.connectionGater
117
+ this.started = false
118
+ this.hopTimeout = init?.hopTimeout ?? DEFAULT_HOP_TIMEOUT
119
+ this.shutdownController = new AbortController()
120
+ this.maxInboundHopStreams = init.maxInboundHopStreams
121
+ this.maxOutboundHopStreams = init.maxOutboundHopStreams
122
+
123
+ try {
124
+ // fails on node < 15.4
125
+ setMaxListeners?.(Infinity, this.shutdownController.signal)
126
+ } catch { }
127
+
128
+ if (init.advertise != null && init.advertise !== false) {
129
+ this.advertService = new AdvertService(components, init.advertise === true ? undefined : init.advertise)
130
+ this.advertService.addEventListener('advert:success', () => {
131
+ this.dispatchEvent(new CustomEvent('relay:advert:success'))
132
+ })
133
+ this.advertService.addEventListener('advert:error', (evt) => {
134
+ this.safeDispatchEvent('relay:advert:error', { detail: evt.detail })
135
+ })
136
+ }
137
+
138
+ this.reservationStore = new ReservationStore(init.reservations)
139
+ }
140
+
141
+ isStarted (): boolean {
142
+ return this.started
143
+ }
144
+
145
+ /**
146
+ * Start Relay service
147
+ */
148
+ async start (): Promise<void> {
149
+ if (this.started) {
150
+ return
151
+ }
152
+
153
+ // Advertise service if HOP enabled and advertising enabled
154
+ this.advertService?.start()
155
+
156
+ await this.registrar.handle(RELAY_V2_HOP_CODEC, (data) => {
157
+ void this.onHop(data).catch(err => {
158
+ log.error(err)
159
+ })
160
+ }, {
161
+ maxInboundStreams: this.maxInboundHopStreams,
162
+ maxOutboundStreams: this.maxOutboundHopStreams
163
+ })
164
+
165
+ this.reservationStore.start()
166
+
167
+ this.started = true
168
+ }
169
+
170
+ /**
171
+ * Stop Relay service
172
+ */
173
+ async stop (): Promise<void> {
174
+ this.advertService?.stop()
175
+ this.reservationStore.stop()
176
+ this.shutdownController.abort()
177
+ await this.registrar.unhandle(RELAY_V2_HOP_CODEC)
178
+
179
+ this.started = false
180
+ }
181
+
182
+ async onHop ({ connection, stream }: IncomingStreamData): Promise<void> {
183
+ log('received circuit v2 hop protocol stream from %s', connection.remotePeer)
184
+
185
+ const hopTimeoutPromise = pDefer<HopMessage>()
186
+ const timeout = setTimeout(() => {
187
+ hopTimeoutPromise.reject('timed out')
188
+ }, this.hopTimeout)
189
+ const pbstr = pbStream(stream)
190
+
191
+ try {
192
+ const request: HopMessage = await Promise.race([
193
+ pbstr.pb(HopMessage).read(),
194
+ hopTimeoutPromise.promise
195
+ ])
196
+
197
+ if (request?.type == null) {
198
+ throw new Error('request was invalid, could not read from stream')
199
+ }
200
+
201
+ log('received', request.type)
202
+
203
+ await Promise.race([
204
+ this.handleHopProtocol({
205
+ connection,
206
+ stream: pbstr,
207
+ request
208
+ }),
209
+ hopTimeoutPromise.promise
210
+ ])
211
+ } catch (err: any) {
212
+ log.error('error while handling hop', err)
213
+ pbstr.pb(HopMessage).write({
214
+ type: HopMessage.Type.STATUS,
215
+ status: Status.MALFORMED_MESSAGE
216
+ })
217
+ stream.abort(err)
218
+ } finally {
219
+ clearTimeout(timeout)
220
+ }
221
+ }
222
+
223
+ async handleHopProtocol ({ stream, request, connection }: HopProtocolOptions): Promise<void> {
224
+ log('received hop message')
225
+ switch (request.type) {
226
+ case HopMessage.Type.RESERVE: await this.handleReserve({ stream, request, connection }); break
227
+ case HopMessage.Type.CONNECT: await this.handleConnect({ stream, request, connection }); break
228
+ default: {
229
+ log.error('invalid hop request type %s via peer %s', request.type, connection.remotePeer)
230
+ stream.pb(HopMessage).write({ type: HopMessage.Type.STATUS, status: Status.UNEXPECTED_MESSAGE })
231
+ }
232
+ }
233
+ }
234
+
235
+ async handleReserve ({ stream, request, connection }: HopProtocolOptions): Promise<void> {
236
+ const hopstr = stream.pb(HopMessage)
237
+ log('hop reserve request from %s', connection.remotePeer)
238
+
239
+ if (isRelayAddr(connection.remoteAddr)) {
240
+ log.error('relay reservation over circuit connection denied for peer: %p', connection.remotePeer)
241
+ hopstr.write({ type: HopMessage.Type.STATUS, status: Status.PERMISSION_DENIED })
242
+ return
243
+ }
244
+
245
+ if ((await this.connectionGater.denyInboundRelayReservation?.(connection.remotePeer)) === true) {
246
+ log.error('reservation for %p denied by connection gater', connection.remotePeer)
247
+ hopstr.write({ type: HopMessage.Type.STATUS, status: Status.PERMISSION_DENIED })
248
+ return
249
+ }
250
+
251
+ const result = this.reservationStore.reserve(connection.remotePeer, connection.remoteAddr)
252
+
253
+ if (result.status !== Status.OK) {
254
+ hopstr.write({ type: HopMessage.Type.STATUS, status: result.status })
255
+ return
256
+ }
257
+
258
+ try {
259
+ // tag relay target peer
260
+ // result.expire is non-null if `ReservationStore.reserve` returns with status == OK
261
+ if (result.expire != null) {
262
+ const ttl = new Date().getTime() - result.expire
263
+ await this.peerStore.tagPeer(connection.remotePeer, RELAY_SOURCE_TAG, { value: 1, ttl })
264
+ }
265
+
266
+ hopstr.write({
267
+ type: HopMessage.Type.STATUS,
268
+ status: Status.OK,
269
+ reservation: await this.makeReservation(connection.remotePeer, BigInt(result.expire ?? 0)),
270
+ limit: this.reservationStore.get(connection.remotePeer)?.limit
271
+ })
272
+ log('sent confirmation response to %s', connection.remotePeer)
273
+ } catch (err) {
274
+ log.error('failed to send confirmation response to %p', connection.remotePeer, err)
275
+ this.reservationStore.removeReservation(connection.remotePeer)
276
+ }
277
+ }
278
+
279
+ async makeReservation (
280
+ remotePeer: PeerId,
281
+ expire: bigint
282
+ ): Promise<Reservation> {
283
+ const addrs = []
284
+
285
+ for (const relayAddr of this.addressManager.getAddresses()) {
286
+ addrs.push(relayAddr.bytes)
287
+ }
288
+
289
+ const voucher = await RecordEnvelope.seal(new ReservationVoucherRecord({
290
+ peer: remotePeer,
291
+ relay: this.peerId,
292
+ expiration: Number(expire)
293
+ }), this.peerId)
294
+
295
+ return {
296
+ addrs,
297
+ expire,
298
+ voucher: voucher.marshal()
299
+ }
300
+ }
301
+
302
+ async handleConnect ({ stream, request, connection }: HopProtocolOptions): Promise<void> {
303
+ const hopstr = stream.pb(HopMessage)
304
+
305
+ if (isRelayAddr(connection.remoteAddr)) {
306
+ log.error('relay reservation over circuit connection denied for peer: %p', connection.remotePeer)
307
+ hopstr.write({ type: HopMessage.Type.STATUS, status: Status.PERMISSION_DENIED })
308
+ return
309
+ }
310
+
311
+ log('hop connect request from %s', connection.remotePeer)
312
+
313
+ let dstPeer: PeerId
314
+
315
+ try {
316
+ if (request.peer == null) {
317
+ log.error('no peer info in hop connect request')
318
+ throw new Error('no peer info in request')
319
+ }
320
+
321
+ request.peer.addrs.forEach(multiaddr)
322
+ dstPeer = peerIdFromBytes(request.peer.id)
323
+ } catch (err) {
324
+ log.error('invalid hop connect request via peer %p %s', connection.remotePeer, err)
325
+ hopstr.write({ type: HopMessage.Type.STATUS, status: Status.MALFORMED_MESSAGE })
326
+ return
327
+ }
328
+
329
+ if (!this.reservationStore.hasReservation(dstPeer)) {
330
+ log.error('hop connect denied for destination peer %p not having a reservation for %p with status %s', dstPeer, connection.remotePeer, Status.NO_RESERVATION)
331
+ hopstr.write({ type: HopMessage.Type.STATUS, status: Status.NO_RESERVATION })
332
+ return
333
+ }
334
+
335
+ if ((await this.connectionGater.denyOutboundRelayedConnection?.(connection.remotePeer, dstPeer)) === true) {
336
+ log.error('hop connect for %p to %p denied by connection gater', connection.remotePeer, dstPeer)
337
+ hopstr.write({ type: HopMessage.Type.STATUS, status: Status.PERMISSION_DENIED })
338
+ return
339
+ }
340
+
341
+ const connections = this.connectionManager.getConnections(dstPeer)
342
+
343
+ if (connections.length === 0) {
344
+ log('hop connect denied for destination peer %p not having a connection for %p as there is no destination connection', dstPeer, connection.remotePeer)
345
+ hopstr.write({ type: HopMessage.Type.STATUS, status: Status.NO_RESERVATION })
346
+ return
347
+ }
348
+
349
+ const destinationConnection = connections[0]
350
+
351
+ const destinationStream = await this.stopHop({
352
+ connection: destinationConnection,
353
+ request: {
354
+ type: StopMessage.Type.CONNECT,
355
+ peer: {
356
+ id: connection.remotePeer.toBytes(),
357
+ addrs: []
358
+ }
359
+ }
360
+ })
361
+
362
+ if (destinationStream == null) {
363
+ log.error('failed to open stream to destination peer %s', destinationConnection?.remotePeer)
364
+ hopstr.write({ type: HopMessage.Type.STATUS, status: Status.CONNECTION_FAILED })
365
+ return
366
+ }
367
+
368
+ hopstr.write({ type: HopMessage.Type.STATUS, status: Status.OK })
369
+ const sourceStream = stream.unwrap()
370
+
371
+ log('connection from %p to %p established - merging streans', connection.remotePeer, dstPeer)
372
+ const limit = this.reservationStore.get(dstPeer)?.limit
373
+ // Short circuit the two streams to create the relayed connection
374
+ createLimitedRelay(sourceStream, destinationStream, this.shutdownController.signal, limit)
375
+ }
376
+
377
+ /**
378
+ * Send a STOP request to the target peer that the dialing peer wants to contact
379
+ */
380
+ async stopHop ({
381
+ connection,
382
+ request
383
+ }: StopOptions): Promise<Stream | undefined> {
384
+ log('starting circuit relay v2 stop request to %s', connection.remotePeer)
385
+ const stream = await connection.newStream([RELAY_V2_STOP_CODEC])
386
+ const pbstr = pbStream(stream)
387
+ const stopstr = pbstr.pb(StopMessage)
388
+ stopstr.write(request)
389
+ let response
390
+
391
+ try {
392
+ response = await stopstr.read()
393
+ } catch (err) {
394
+ log.error('error parsing stop message response from %s', connection.remotePeer)
395
+ }
396
+
397
+ if (response == null) {
398
+ log.error('could not read response from %s', connection.remotePeer)
399
+ stream.close()
400
+ return
401
+ }
402
+
403
+ if (response.status === Status.OK) {
404
+ log('stop request to %s was successful', connection.remotePeer)
405
+ return pbstr.unwrap()
406
+ }
407
+
408
+ log('stop request failed with code %d', response.status)
409
+ stream.close()
410
+ }
411
+
412
+ get reservations (): PeerMap<RelayReservation> {
413
+ return this.reservationStore.reservations
414
+ }
415
+ }
416
+
417
+ export function circuitRelayServer (init: CircuitRelayServerInit = {}): (components: CircuitRelayServerComponents) => CircuitRelayService {
418
+ return (components) => {
419
+ return new CircuitRelayServer(components, init)
420
+ }
421
+ }
@@ -0,0 +1,111 @@
1
+ import { Limit, Status } from '../pb/index.js'
2
+ import type { Multiaddr } from '@multiformats/multiaddr'
3
+ import type { PeerId } from '@libp2p/interface-peer-id'
4
+ import type { Startable } from '@libp2p/interfaces/startable'
5
+ import { PeerMap } from '@libp2p/peer-collections'
6
+ import type { RecursivePartial } from '@libp2p/interfaces'
7
+ import { DEFAULT_DATA_LIMIT, DEFAULT_DURATION_LIMIT, DEFAULT_MAX_RESERVATION_CLEAR_INTERVAL, DEFAULT_MAX_RESERVATION_STORE_SIZE, DEFAULT_MAX_RESERVATION_TTL } from '../constants.js'
8
+ import type { RelayReservation } from '../index.js'
9
+
10
+ export type ReservationStatus = Status.OK | Status.PERMISSION_DENIED | Status.RESERVATION_REFUSED
11
+
12
+ export interface ReservationStoreInit {
13
+ /*
14
+ * maximum number of reservations allowed, default: 15
15
+ */
16
+ maxReservations?: number
17
+ /*
18
+ * interval after which stale reservations are cleared, default: 300s
19
+ */
20
+ reservationClearInterval?: number
21
+ /*
22
+ * apply default relay limits to a new reservation, default: true
23
+ */
24
+ applyDefaultLimit?: boolean
25
+ /**
26
+ * reservation ttl, default: 2 hours
27
+ */
28
+ reservationTtl?: number
29
+ /**
30
+ * The maximum time a relayed connection can be open for
31
+ */
32
+ defaultDurationLimit?: number
33
+ /**
34
+ * The maximum amount of data allowed to be transferred over a relayed connection
35
+ */
36
+ defaultDataLimit?: bigint
37
+ }
38
+
39
+ export type ReservationStoreOptions = RecursivePartial<ReservationStoreInit>
40
+
41
+ export class ReservationStore implements Startable {
42
+ public readonly reservations = new PeerMap<RelayReservation>()
43
+ private _started = false
44
+ private interval: any
45
+ private readonly maxReservations: number
46
+ private readonly reservationClearInterval: number
47
+ private readonly applyDefaultLimit: boolean
48
+ private readonly reservationTtl: number
49
+ private readonly defaultDurationLimit: number
50
+ private readonly defaultDataLimit: bigint
51
+
52
+ constructor (options: ReservationStoreOptions = {}) {
53
+ this.maxReservations = options.maxReservations ?? DEFAULT_MAX_RESERVATION_STORE_SIZE
54
+ this.reservationClearInterval = options.reservationClearInterval ?? DEFAULT_MAX_RESERVATION_CLEAR_INTERVAL
55
+ this.applyDefaultLimit = options.applyDefaultLimit !== false
56
+ this.reservationTtl = options.reservationTtl ?? DEFAULT_MAX_RESERVATION_TTL
57
+ this.defaultDurationLimit = options.defaultDurationLimit ?? DEFAULT_DURATION_LIMIT
58
+ this.defaultDataLimit = options.defaultDataLimit ?? DEFAULT_DATA_LIMIT
59
+ }
60
+
61
+ isStarted (): boolean {
62
+ return this._started
63
+ }
64
+
65
+ start (): void {
66
+ if (this._started) {
67
+ return
68
+ }
69
+ this._started = true
70
+ this.interval = setInterval(
71
+ () => {
72
+ const now = (new Date()).getTime()
73
+ this.reservations.forEach((r, k) => {
74
+ if (r.expire.getTime() < now) {
75
+ this.reservations.delete(k)
76
+ }
77
+ })
78
+ },
79
+ this.reservationClearInterval
80
+ )
81
+ }
82
+
83
+ stop (): void {
84
+ clearInterval(this.interval)
85
+ }
86
+
87
+ reserve (peer: PeerId, addr: Multiaddr, limit?: Limit): { status: ReservationStatus, expire?: number } {
88
+ if (this.reservations.size >= this.maxReservations && !this.reservations.has(peer)) {
89
+ return { status: Status.RESERVATION_REFUSED }
90
+ }
91
+ const expire = new Date(Date.now() + this.reservationTtl)
92
+ let checkedLimit: Limit | undefined
93
+ if (this.applyDefaultLimit) {
94
+ checkedLimit = limit ?? { data: this.defaultDataLimit, duration: this.defaultDurationLimit }
95
+ }
96
+ this.reservations.set(peer, { addr, expire, limit: checkedLimit })
97
+ return { status: Status.OK, expire: expire.getTime() }
98
+ }
99
+
100
+ removeReservation (peer: PeerId): void {
101
+ this.reservations.delete(peer)
102
+ }
103
+
104
+ hasReservation (dst: PeerId): boolean {
105
+ return this.reservations.has(dst)
106
+ }
107
+
108
+ get (peer: PeerId): RelayReservation | undefined {
109
+ return this.reservations.get(peer)
110
+ }
111
+ }
@@ -0,0 +1,51 @@
1
+ import type { PeerId } from '@libp2p/interface-peer-id'
2
+ import type { Record } from '@libp2p/interface-record'
3
+ import { ReservationVoucher } from '../pb/index.js'
4
+
5
+ export interface ReservationVoucherOptions {
6
+ relay: PeerId
7
+ peer: PeerId
8
+ expiration: number
9
+ }
10
+
11
+ export class ReservationVoucherRecord implements Record {
12
+ public readonly domain = 'libp2p-relay-rsvp'
13
+ public readonly codec = new Uint8Array([0x03, 0x02])
14
+
15
+ private readonly relay: PeerId
16
+ private readonly peer: PeerId
17
+ private readonly expiration: number
18
+
19
+ constructor ({ relay, peer, expiration }: ReservationVoucherOptions) {
20
+ this.relay = relay
21
+ this.peer = peer
22
+ this.expiration = expiration
23
+ }
24
+
25
+ marshal (): Uint8Array {
26
+ return ReservationVoucher.encode({
27
+ relay: this.relay.toBytes(),
28
+ peer: this.peer.toBytes(),
29
+ expiration: BigInt(this.expiration)
30
+ })
31
+ }
32
+
33
+ equals (other: Record): boolean {
34
+ if (!(other instanceof ReservationVoucherRecord)) {
35
+ return false
36
+ }
37
+ if (!this.peer.equals(other.peer)) {
38
+ return false
39
+ }
40
+
41
+ if (!this.relay.equals(other.relay)) {
42
+ return false
43
+ }
44
+
45
+ if (this.expiration !== other.expiration) {
46
+ return false
47
+ }
48
+
49
+ return true
50
+ }
51
+ }