@streamr/trackerless-network 0.0.1-tatum.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 (262) hide show
  1. package/.eslintignore +7 -0
  2. package/.eslintrc +3 -0
  3. package/README.md +6 -0
  4. package/bin/bootstrap-node.ts +73 -0
  5. package/bin/full-node-webrtc.ts +102 -0
  6. package/bin/full-node-websocket.ts +102 -0
  7. package/bin/network.ts +43 -0
  8. package/dist/package.json +53 -0
  9. package/dist/src/NameDirectory.d.ts +5 -0
  10. package/dist/src/NameDirectory.js +44 -0
  11. package/dist/src/NameDirectory.js.map +1 -0
  12. package/dist/src/NetworkNode.d.ts +35 -0
  13. package/dist/src/NetworkNode.js +130 -0
  14. package/dist/src/NetworkNode.js.map +1 -0
  15. package/dist/src/NetworkStack.d.ts +32 -0
  16. package/dist/src/NetworkStack.js +108 -0
  17. package/dist/src/NetworkStack.js.map +1 -0
  18. package/dist/src/exports.d.ts +6 -0
  19. package/dist/src/exports.js +12 -0
  20. package/dist/src/exports.js.map +1 -0
  21. package/dist/src/identifiers.d.ts +1 -0
  22. package/dist/src/identifiers.js +3 -0
  23. package/dist/src/identifiers.js.map +1 -0
  24. package/dist/src/logic/DuplicateMessageDetector.d.ts +55 -0
  25. package/dist/src/logic/DuplicateMessageDetector.js +155 -0
  26. package/dist/src/logic/DuplicateMessageDetector.js.map +1 -0
  27. package/dist/src/logic/ILayer0.d.ts +13 -0
  28. package/dist/src/logic/ILayer0.js +3 -0
  29. package/dist/src/logic/ILayer0.js.map +1 -0
  30. package/dist/src/logic/IStreamNode.d.ts +12 -0
  31. package/dist/src/logic/IStreamNode.js +3 -0
  32. package/dist/src/logic/IStreamNode.js.map +1 -0
  33. package/dist/src/logic/PeerList.d.ts +27 -0
  34. package/dist/src/logic/PeerList.js +84 -0
  35. package/dist/src/logic/PeerList.js.map +1 -0
  36. package/dist/src/logic/RandomGraphNode.d.ts +68 -0
  37. package/dist/src/logic/RandomGraphNode.js +201 -0
  38. package/dist/src/logic/RandomGraphNode.js.map +1 -0
  39. package/dist/src/logic/Remote.d.ts +9 -0
  40. package/dist/src/logic/Remote.js +15 -0
  41. package/dist/src/logic/Remote.js.map +1 -0
  42. package/dist/src/logic/RemoteRandomGraphNode.d.ts +8 -0
  43. package/dist/src/logic/RemoteRandomGraphNode.js +35 -0
  44. package/dist/src/logic/RemoteRandomGraphNode.js.map +1 -0
  45. package/dist/src/logic/StreamEntryPointDiscovery.d.ts +36 -0
  46. package/dist/src/logic/StreamEntryPointDiscovery.js +179 -0
  47. package/dist/src/logic/StreamEntryPointDiscovery.js.map +1 -0
  48. package/dist/src/logic/StreamNodeServer.d.ts +20 -0
  49. package/dist/src/logic/StreamNodeServer.js +26 -0
  50. package/dist/src/logic/StreamNodeServer.js.map +1 -0
  51. package/dist/src/logic/StreamrNode.d.ts +76 -0
  52. package/dist/src/logic/StreamrNode.js +303 -0
  53. package/dist/src/logic/StreamrNode.js.map +1 -0
  54. package/dist/src/logic/createRandomGraphNode.d.ts +5 -0
  55. package/dist/src/logic/createRandomGraphNode.js +110 -0
  56. package/dist/src/logic/createRandomGraphNode.js.map +1 -0
  57. package/dist/src/logic/inspect/InspectSession.d.ts +18 -0
  58. package/dist/src/logic/inspect/InspectSession.js +38 -0
  59. package/dist/src/logic/inspect/InspectSession.js.map +1 -0
  60. package/dist/src/logic/inspect/Inspector.d.ts +33 -0
  61. package/dist/src/logic/inspect/Inspector.js +63 -0
  62. package/dist/src/logic/inspect/Inspector.js.map +1 -0
  63. package/dist/src/logic/neighbor-discovery/Handshaker.d.ts +35 -0
  64. package/dist/src/logic/neighbor-discovery/Handshaker.js +121 -0
  65. package/dist/src/logic/neighbor-discovery/Handshaker.js.map +1 -0
  66. package/dist/src/logic/neighbor-discovery/HandshakerServer.d.ts +30 -0
  67. package/dist/src/logic/neighbor-discovery/HandshakerServer.js +78 -0
  68. package/dist/src/logic/neighbor-discovery/HandshakerServer.js.map +1 -0
  69. package/dist/src/logic/neighbor-discovery/NeighborFinder.d.ts +23 -0
  70. package/dist/src/logic/neighbor-discovery/NeighborFinder.js +44 -0
  71. package/dist/src/logic/neighbor-discovery/NeighborFinder.js.map +1 -0
  72. package/dist/src/logic/neighbor-discovery/NeighborUpdateManager.d.ts +30 -0
  73. package/dist/src/logic/neighbor-discovery/NeighborUpdateManager.js +42 -0
  74. package/dist/src/logic/neighbor-discovery/NeighborUpdateManager.js.map +1 -0
  75. package/dist/src/logic/neighbor-discovery/NeighborUpdateManagerServer.d.ts +20 -0
  76. package/dist/src/logic/neighbor-discovery/NeighborUpdateManagerServer.js +42 -0
  77. package/dist/src/logic/neighbor-discovery/NeighborUpdateManagerServer.js.map +1 -0
  78. package/dist/src/logic/neighbor-discovery/RemoteHandshaker.d.ts +12 -0
  79. package/dist/src/logic/neighbor-discovery/RemoteHandshaker.js +54 -0
  80. package/dist/src/logic/neighbor-discovery/RemoteHandshaker.js.map +1 -0
  81. package/dist/src/logic/neighbor-discovery/RemoteNeighborUpdateManager.d.ts +11 -0
  82. package/dist/src/logic/neighbor-discovery/RemoteNeighborUpdateManager.js +37 -0
  83. package/dist/src/logic/neighbor-discovery/RemoteNeighborUpdateManager.js.map +1 -0
  84. package/dist/src/logic/propagation/FifoMapWithTTL.d.ts +36 -0
  85. package/dist/src/logic/propagation/FifoMapWithTTL.js +81 -0
  86. package/dist/src/logic/propagation/FifoMapWithTTL.js.map +1 -0
  87. package/dist/src/logic/propagation/Propagation.d.ts +31 -0
  88. package/dist/src/logic/propagation/Propagation.js +64 -0
  89. package/dist/src/logic/propagation/Propagation.js.map +1 -0
  90. package/dist/src/logic/propagation/PropagationTaskStore.d.ts +21 -0
  91. package/dist/src/logic/propagation/PropagationTaskStore.js +32 -0
  92. package/dist/src/logic/propagation/PropagationTaskStore.js.map +1 -0
  93. package/dist/src/logic/protocol-integration/stream-message/ContentMessageTranslator.d.ts +5 -0
  94. package/dist/src/logic/protocol-integration/stream-message/ContentMessageTranslator.js +17 -0
  95. package/dist/src/logic/protocol-integration/stream-message/ContentMessageTranslator.js.map +1 -0
  96. package/dist/src/logic/protocol-integration/stream-message/GroupKeyRequestTranslator.d.ts +6 -0
  97. package/dist/src/logic/protocol-integration/stream-message/GroupKeyRequestTranslator.js +27 -0
  98. package/dist/src/logic/protocol-integration/stream-message/GroupKeyRequestTranslator.js.map +1 -0
  99. package/dist/src/logic/protocol-integration/stream-message/GroupKeyResponseTranslator.d.ts +6 -0
  100. package/dist/src/logic/protocol-integration/stream-message/GroupKeyResponseTranslator.js +33 -0
  101. package/dist/src/logic/protocol-integration/stream-message/GroupKeyResponseTranslator.js.map +1 -0
  102. package/dist/src/logic/protocol-integration/stream-message/StreamMessageTranslator.d.ts +6 -0
  103. package/dist/src/logic/protocol-integration/stream-message/StreamMessageTranslator.js +109 -0
  104. package/dist/src/logic/protocol-integration/stream-message/StreamMessageTranslator.js.map +1 -0
  105. package/dist/src/logic/proxy/ProxyStreamConnectionClient.d.ts +44 -0
  106. package/dist/src/logic/proxy/ProxyStreamConnectionClient.js +189 -0
  107. package/dist/src/logic/proxy/ProxyStreamConnectionClient.js.map +1 -0
  108. package/dist/src/logic/proxy/ProxyStreamConnectionServer.d.ts +34 -0
  109. package/dist/src/logic/proxy/ProxyStreamConnectionServer.js +64 -0
  110. package/dist/src/logic/proxy/ProxyStreamConnectionServer.js.map +1 -0
  111. package/dist/src/logic/proxy/RemoteProxyServer.d.ts +7 -0
  112. package/dist/src/logic/proxy/RemoteProxyServer.js +36 -0
  113. package/dist/src/logic/proxy/RemoteProxyServer.js.map +1 -0
  114. package/dist/src/logic/temporary-connection/RemoteTemporaryConnectionRpcServer.d.ts +6 -0
  115. package/dist/src/logic/temporary-connection/RemoteTemporaryConnectionRpcServer.js +28 -0
  116. package/dist/src/logic/temporary-connection/RemoteTemporaryConnectionRpcServer.js.map +1 -0
  117. package/dist/src/logic/temporary-connection/TemporaryConnectionRpcServer.d.ts +20 -0
  118. package/dist/src/logic/temporary-connection/TemporaryConnectionRpcServer.js +29 -0
  119. package/dist/src/logic/temporary-connection/TemporaryConnectionRpcServer.js.map +1 -0
  120. package/dist/src/logic/utils.d.ts +3 -0
  121. package/dist/src/logic/utils.js +16 -0
  122. package/dist/src/logic/utils.js.map +1 -0
  123. package/dist/src/proto/google/protobuf/any.d.ts +173 -0
  124. package/dist/src/proto/google/protobuf/any.js +155 -0
  125. package/dist/src/proto/google/protobuf/any.js.map +1 -0
  126. package/dist/src/proto/google/protobuf/empty.d.ts +32 -0
  127. package/dist/src/proto/google/protobuf/empty.js +34 -0
  128. package/dist/src/proto/google/protobuf/empty.js.map +1 -0
  129. package/dist/src/proto/google/protobuf/timestamp.d.ts +149 -0
  130. package/dist/src/proto/google/protobuf/timestamp.js +136 -0
  131. package/dist/src/proto/google/protobuf/timestamp.js.map +1 -0
  132. package/dist/src/proto/packages/dht/protos/DhtRpc.client.d.ts +320 -0
  133. package/dist/src/proto/packages/dht/protos/DhtRpc.client.js +245 -0
  134. package/dist/src/proto/packages/dht/protos/DhtRpc.client.js.map +1 -0
  135. package/dist/src/proto/packages/dht/protos/DhtRpc.d.ts +1089 -0
  136. package/dist/src/proto/packages/dht/protos/DhtRpc.js +710 -0
  137. package/dist/src/proto/packages/dht/protos/DhtRpc.js.map +1 -0
  138. package/dist/src/proto/packages/dht/protos/DhtRpc.server.d.ts +145 -0
  139. package/dist/src/proto/packages/dht/protos/DhtRpc.server.js +3 -0
  140. package/dist/src/proto/packages/dht/protos/DhtRpc.server.js.map +1 -0
  141. package/dist/src/proto/packages/proto-rpc/protos/ProtoRpc.d.ts +87 -0
  142. package/dist/src/proto/packages/proto-rpc/protos/ProtoRpc.js +66 -0
  143. package/dist/src/proto/packages/proto-rpc/protos/ProtoRpc.js.map +1 -0
  144. package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.client.d.ts +156 -0
  145. package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.client.js +122 -0
  146. package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.client.js.map +1 -0
  147. package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.d.ts +524 -0
  148. package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.js +350 -0
  149. package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.js.map +1 -0
  150. package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.server.d.ts +65 -0
  151. package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.server.js +3 -0
  152. package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.server.js.map +1 -0
  153. package/dist/test/benchmark/first-message.d.ts +1 -0
  154. package/dist/test/benchmark/first-message.js +137 -0
  155. package/dist/test/benchmark/first-message.js.map +1 -0
  156. package/dist/test/utils/utils.d.ts +12 -0
  157. package/dist/test/utils/utils.js +86 -0
  158. package/dist/test/utils/utils.js.map +1 -0
  159. package/jest.config.js +36 -0
  160. package/karma.config.js +20 -0
  161. package/log.txt +501 -0
  162. package/package.json +53 -0
  163. package/proto.sh +2 -0
  164. package/protos/NetworkRpc.proto +161 -0
  165. package/src/NameDirectory.ts +44 -0
  166. package/src/NetworkNode.ts +169 -0
  167. package/src/NetworkStack.ts +144 -0
  168. package/src/exports.ts +6 -0
  169. package/src/identifiers.ts +1 -0
  170. package/src/logic/DuplicateMessageDetector.ts +167 -0
  171. package/src/logic/ILayer0.ts +14 -0
  172. package/src/logic/IStreamNode.ts +17 -0
  173. package/src/logic/PeerList.ts +106 -0
  174. package/src/logic/RandomGraphNode.ts +310 -0
  175. package/src/logic/Remote.ts +19 -0
  176. package/src/logic/RemoteRandomGraphNode.ts +39 -0
  177. package/src/logic/StreamEntryPointDiscovery.ts +221 -0
  178. package/src/logic/StreamNodeServer.ts +44 -0
  179. package/src/logic/StreamrNode.ts +416 -0
  180. package/src/logic/createRandomGraphNode.ts +114 -0
  181. package/src/logic/inspect/InspectSession.ts +49 -0
  182. package/src/logic/inspect/Inspector.ts +89 -0
  183. package/src/logic/neighbor-discovery/Handshaker.ts +180 -0
  184. package/src/logic/neighbor-discovery/HandshakerServer.ts +99 -0
  185. package/src/logic/neighbor-discovery/NeighborFinder.ts +61 -0
  186. package/src/logic/neighbor-discovery/NeighborUpdateManager.ts +67 -0
  187. package/src/logic/neighbor-discovery/NeighborUpdateManagerServer.ts +61 -0
  188. package/src/logic/neighbor-discovery/RemoteHandshaker.ts +64 -0
  189. package/src/logic/neighbor-discovery/RemoteNeighborUpdateManager.ts +41 -0
  190. package/src/logic/propagation/FifoMapWithTTL.ts +108 -0
  191. package/src/logic/propagation/Propagation.ts +83 -0
  192. package/src/logic/propagation/PropagationTaskStore.ts +40 -0
  193. package/src/logic/protocol-integration/stream-message/ContentMessageTranslator.ts +16 -0
  194. package/src/logic/protocol-integration/stream-message/GroupKeyRequestTranslator.ts +28 -0
  195. package/src/logic/protocol-integration/stream-message/GroupKeyResponseTranslator.ts +38 -0
  196. package/src/logic/protocol-integration/stream-message/StreamMessageTranslator.ts +142 -0
  197. package/src/logic/proxy/ProxyStreamConnectionClient.ts +255 -0
  198. package/src/logic/proxy/ProxyStreamConnectionServer.ts +97 -0
  199. package/src/logic/proxy/RemoteProxyServer.ts +36 -0
  200. package/src/logic/temporary-connection/RemoteTemporaryConnectionRpcServer.ts +27 -0
  201. package/src/logic/temporary-connection/TemporaryConnectionRpcServer.ts +50 -0
  202. package/src/logic/utils.ts +17 -0
  203. package/src/proto/google/protobuf/any.ts +319 -0
  204. package/src/proto/google/protobuf/empty.ts +84 -0
  205. package/src/proto/google/protobuf/timestamp.ts +281 -0
  206. package/src/proto/packages/dht/protos/DhtRpc.client.ts +373 -0
  207. package/src/proto/packages/dht/protos/DhtRpc.server.ts +148 -0
  208. package/src/proto/packages/dht/protos/DhtRpc.ts +1399 -0
  209. package/src/proto/packages/proto-rpc/protos/ProtoRpc.ts +108 -0
  210. package/src/proto/packages/trackerless-network/protos/NetworkRpc.client.ts +176 -0
  211. package/src/proto/packages/trackerless-network/protos/NetworkRpc.server.ts +68 -0
  212. package/src/proto/packages/trackerless-network/protos/NetworkRpc.ts +667 -0
  213. package/test/benchmark/first-message.ts +155 -0
  214. package/test/end-to-end/inspect.test.ts +119 -0
  215. package/test/end-to-end/proxy-and-full-node.test.ts +140 -0
  216. package/test/end-to-end/proxy-connections.test.ts +228 -0
  217. package/test/end-to-end/proxy-key-exchange.test.ts +142 -0
  218. package/test/end-to-end/random-graph-with-real-connections.test.ts +154 -0
  219. package/test/end-to-end/webrtc-full-node-network.test.ts +97 -0
  220. package/test/end-to-end/websocket-full-node-network.test.ts +93 -0
  221. package/test/integration/Handshakes.test.ts +167 -0
  222. package/test/integration/Inspect.test.ts +102 -0
  223. package/test/integration/NetworkNode.test.ts +99 -0
  224. package/test/integration/NetworkRpc.test.ts +61 -0
  225. package/test/integration/NetworkStack.test.ts +74 -0
  226. package/test/integration/NetworkStackStoppedDuringStart.test.ts +45 -0
  227. package/test/integration/Propagation.test.ts +79 -0
  228. package/test/integration/RandomGraphNode-Layer1Node-Latencies.test.ts +141 -0
  229. package/test/integration/RandomGraphNode-Layer1Node.test.ts +226 -0
  230. package/test/integration/RemoteHandshaker.test.ts +78 -0
  231. package/test/integration/RemoteNeighborUpdateManager.test.ts +82 -0
  232. package/test/integration/RemoteRandomGraphNode.test.ts +102 -0
  233. package/test/integration/StreamrNode.test.ts +145 -0
  234. package/test/integration/stream-without-default-entrypoints.test.ts +132 -0
  235. package/test/unit/ContentMessageTranslator.test.ts +20 -0
  236. package/test/unit/DuplicateMessageDetector.test.ts +192 -0
  237. package/test/unit/FifoMapWithTtl.test.ts +229 -0
  238. package/test/unit/GroupKeyRequestTranslator.test.ts +36 -0
  239. package/test/unit/GroupKeyResponseTranslator.test.ts +39 -0
  240. package/test/unit/Handshaker.test.ts +63 -0
  241. package/test/unit/HandshakerServer.test.ts +123 -0
  242. package/test/unit/InspectSession.test.ts +78 -0
  243. package/test/unit/Inspector.test.ts +57 -0
  244. package/test/unit/NeighborFinder.test.ts +48 -0
  245. package/test/unit/NumberPair.test.ts +22 -0
  246. package/test/unit/PeerList.test.ts +150 -0
  247. package/test/unit/Propagation.test.ts +134 -0
  248. package/test/unit/RandomGraphNode.test.ts +73 -0
  249. package/test/unit/StreamEntrypointDiscovery.test.ts +152 -0
  250. package/test/unit/StreamMessageTranslator.test.ts +67 -0
  251. package/test/unit/StreamNodeServer.test.ts +63 -0
  252. package/test/unit/StreamrNode.test.ts +74 -0
  253. package/test/utils/mock/MockHandshaker.ts +15 -0
  254. package/test/utils/mock/MockLayer0.ts +71 -0
  255. package/test/utils/mock/MockLayer1.ts +6 -0
  256. package/test/utils/mock/MockNeighborFinder.ts +19 -0
  257. package/test/utils/mock/MockNeighborUpdateManager.ts +21 -0
  258. package/test/utils/mock/Transport.ts +25 -0
  259. package/test/utils/utils.ts +104 -0
  260. package/tsconfig.browser.json +12 -0
  261. package/tsconfig.jest.json +15 -0
  262. package/tsconfig.node.json +18 -0
@@ -0,0 +1,221 @@
1
+ import { createHash } from 'crypto'
2
+ import {
3
+ isSamePeerDescriptor,
4
+ PeerDescriptor,
5
+ RecursiveFindResult,
6
+ DataEntry
7
+ } from '@streamr/dht'
8
+ import { Any } from '../proto/google/protobuf/any'
9
+ import { Logger, setAbortableTimeout, wait } from '@streamr/utils'
10
+ import { StreamObject } from './StreamrNode'
11
+
12
+ export const streamPartIdToDataKey = (streamPartId: string): Uint8Array => {
13
+ return new Uint8Array(createHash('md5').update(streamPartId).digest())
14
+ }
15
+
16
+ const parseEntryPointData = (dataEntries: DataEntry[]): PeerDescriptor[] => {
17
+ return dataEntries!.filter((entry) => !entry.deleted).map((entry) => Any.unpack(entry.data!, PeerDescriptor))
18
+ }
19
+
20
+ interface FindEntryPointsResult {
21
+ joiningEmptyStream: boolean
22
+ entryPointsFromDht: boolean
23
+ discoveredEntryPoints: PeerDescriptor[]
24
+ }
25
+
26
+ const exponentialRunOff = async (
27
+ task: () => Promise<void>,
28
+ description: string,
29
+ abortSignal: AbortSignal,
30
+ baseDelay = 500,
31
+ maxAttempts = 6
32
+ ): Promise<void> => {
33
+ for (let i = 1; i <= maxAttempts; i++) {
34
+ if (abortSignal.aborted) {
35
+ return
36
+ }
37
+ const factor = 2 ** i
38
+ const delay = baseDelay * factor
39
+ try {
40
+ await task()
41
+ } catch (e: any) {
42
+ logger.trace(`${description} failed, retrying in ${delay} ms`)
43
+ }
44
+ try { // Abort controller throws unexpected errors in destroy?
45
+ await wait(delay, abortSignal)
46
+ } catch (err) {
47
+ logger.trace(`${err}`)
48
+ }
49
+ }
50
+ }
51
+
52
+ const logger = new Logger(module)
53
+
54
+ const ENTRYPOINT_STORE_LIMIT = 8
55
+
56
+ interface StreamEntryPointDiscoveryConfig {
57
+ streams: Map<string, StreamObject>
58
+ ownPeerDescriptor: PeerDescriptor
59
+ getEntryPointData: (key: Uint8Array) => Promise<RecursiveFindResult>
60
+ getEntryPointDataViaPeer: (key: Uint8Array, peer: PeerDescriptor) => Promise<DataEntry[]>
61
+ storeEntryPointData: (key: Uint8Array, data: Any) => Promise<PeerDescriptor[]>
62
+ deleteEntryPointData: (key: Uint8Array) => Promise<void>
63
+ cacheInterval?: number
64
+ }
65
+
66
+ export class StreamEntryPointDiscovery {
67
+ private readonly abortController: AbortController
68
+ private readonly config: StreamEntryPointDiscoveryConfig
69
+ private readonly servicedStreams: Map<string, NodeJS.Timeout>
70
+ private readonly cacheInterval: number
71
+
72
+ constructor(config: StreamEntryPointDiscoveryConfig) {
73
+ this.config = config
74
+ this.abortController = new AbortController()
75
+ this.cacheInterval = this.config.cacheInterval ?? 60000
76
+ this.servicedStreams = new Map()
77
+ }
78
+
79
+ async discoverEntryPointsFromDht(
80
+ streamPartID: string,
81
+ knownEntryPointCount: number,
82
+ forwardingPeer?: PeerDescriptor
83
+ ): Promise<FindEntryPointsResult> {
84
+ if (knownEntryPointCount > 0) {
85
+ return {
86
+ joiningEmptyStream: false,
87
+ entryPointsFromDht: false,
88
+ discoveredEntryPoints: []
89
+ }
90
+ }
91
+ let joiningEmptyStream = false
92
+ const discoveredEntryPoints = await this.discoverEntryPoints(streamPartID, forwardingPeer)
93
+ if (discoveredEntryPoints.length === 0) {
94
+ joiningEmptyStream = true
95
+ discoveredEntryPoints.push(this.config.ownPeerDescriptor)
96
+ }
97
+ return {
98
+ joiningEmptyStream,
99
+ discoveredEntryPoints,
100
+ entryPointsFromDht: true
101
+ }
102
+ }
103
+
104
+ private async discoverEntryPoints(streamPartId: string, forwardingPeer?: PeerDescriptor): Promise<PeerDescriptor[]> {
105
+ const dataKey = streamPartIdToDataKey(streamPartId)
106
+ return forwardingPeer ?
107
+ this.queryEntryPointsViaPeer(dataKey, forwardingPeer) : await this.queryEntrypoints(dataKey)
108
+ }
109
+
110
+ private async queryEntrypoints(key: Uint8Array): Promise<PeerDescriptor[]> {
111
+ logger.trace(`Finding data from dht peer ${this.config.ownPeerDescriptor!.nodeName}`)
112
+ try {
113
+ const results = await this.config.getEntryPointData(key)
114
+ if (results.dataEntries) {
115
+ return parseEntryPointData(results.dataEntries)
116
+ } else {
117
+ return []
118
+ }
119
+ } catch (err) {
120
+ return []
121
+ }
122
+ }
123
+
124
+ private async queryEntryPointsViaPeer(key: Uint8Array, peer: PeerDescriptor): Promise<PeerDescriptor[]> {
125
+ logger.trace(`Finding data via peer ${this.config.ownPeerDescriptor!.nodeName}`)
126
+ try {
127
+ const results = await this.config.getEntryPointDataViaPeer(key, peer)
128
+ if (results) {
129
+ return parseEntryPointData(results)
130
+ } else {
131
+ return []
132
+ }
133
+ } catch (err) {
134
+ return []
135
+ }
136
+ }
137
+
138
+ async storeSelfAsEntryPointIfNecessary(
139
+ streamPartID: string,
140
+ joiningEmptyStream: boolean,
141
+ entryPointsFromDht: boolean,
142
+ currentEntrypointCount: number
143
+ ): Promise<void> {
144
+ if (joiningEmptyStream) {
145
+ await this.storeSelfAsEntryPoint(streamPartID)
146
+ setImmediate(() => this.avoidNetworkSplit(streamPartID))
147
+ } else if (entryPointsFromDht && currentEntrypointCount < ENTRYPOINT_STORE_LIMIT) {
148
+ try {
149
+ await this.storeSelfAsEntryPoint(streamPartID)
150
+ } catch (err) {
151
+ logger.trace(`Failed to store self as entrypoint on stream `)
152
+ }
153
+ }
154
+ }
155
+
156
+ private async storeSelfAsEntryPoint(streamPartId: string): Promise<void> {
157
+ const ownPeerDescriptor = this.config.ownPeerDescriptor
158
+ const dataToStore = Any.pack(ownPeerDescriptor, PeerDescriptor)
159
+ try {
160
+ await this.config.storeEntryPointData(streamPartIdToDataKey(streamPartId), dataToStore)
161
+ this.keepSelfAsEntryPoint(streamPartId)
162
+ } catch (err) {
163
+ logger.warn(`Failed to store self as entrypoint for ${streamPartId}`)
164
+ }
165
+ }
166
+
167
+ private keepSelfAsEntryPoint(streamPartId: string): void {
168
+ if (!this.config.streams.has(streamPartId) || this.servicedStreams.has(streamPartId)) {
169
+ return
170
+ }
171
+ this.servicedStreams.set(streamPartId, setTimeout(async () => {
172
+ if (!this.config.streams.has(streamPartId)) {
173
+ this.servicedStreams.delete(streamPartId)
174
+ return
175
+ }
176
+ logger.trace(`Attempting to keep self as entrypoint for ${streamPartId}`)
177
+ try {
178
+ const discovered = await this.discoverEntryPoints(streamPartId)
179
+ if (discovered.length < ENTRYPOINT_STORE_LIMIT
180
+ || discovered.some((peer) => isSamePeerDescriptor(peer, this.config.ownPeerDescriptor))) {
181
+ await this.storeSelfAsEntryPoint(streamPartId)
182
+ this.servicedStreams.delete(streamPartId)
183
+ this.keepSelfAsEntryPoint(streamPartId)
184
+ } else {
185
+ this.servicedStreams.delete(streamPartId)
186
+ }
187
+ } catch (err) {
188
+ logger.debug(`Failed to keep self as entrypoint for ${streamPartId}`)
189
+ }
190
+ }, this.cacheInterval))
191
+ }
192
+
193
+ private async avoidNetworkSplit(streamPartID: string): Promise<void> {
194
+ await exponentialRunOff(async () => {
195
+ if (this.config.streams.has(streamPartID)) {
196
+ const stream = this.config.streams.get(streamPartID)
197
+ const rediscoveredEntrypoints = await this.discoverEntryPoints(streamPartID)
198
+ await stream!.layer1!.joinDht(rediscoveredEntrypoints, false)
199
+ if (stream!.layer1!.getBucketSize() < 4) {
200
+ throw new Error(`Network split is still possible`)
201
+ }
202
+ }
203
+ }, 'avoid network split', this.abortController.signal)
204
+ logger.trace(`Network split avoided`)
205
+ }
206
+
207
+ removeSelfAsEntryPoint(streamPartId: string): void {
208
+ if (this.servicedStreams.has(streamPartId)) {
209
+ setAbortableTimeout(() => this.config.deleteEntryPointData(streamPartIdToDataKey(streamPartId)), 0, this.abortController.signal)
210
+ clearTimeout(this.servicedStreams.get(streamPartId)!)
211
+ this.servicedStreams.delete(streamPartId)
212
+ }
213
+ }
214
+
215
+ async destroy(): Promise<void> {
216
+ this.servicedStreams.forEach((_, streamPartId) => this.removeSelfAsEntryPoint(streamPartId))
217
+ this.servicedStreams.clear()
218
+ this.abortController.abort()
219
+ }
220
+
221
+ }
@@ -0,0 +1,44 @@
1
+ import { keyFromPeerDescriptor, ListeningRpcCommunicator, PeerDescriptor, DhtCallContext, PeerIDKey } from '@streamr/dht'
2
+ import { Empty } from '../proto/google/protobuf/empty'
3
+ import {
4
+ LeaveStreamNotice,
5
+ MessageRef,
6
+ StreamMessage
7
+ } from '../proto/packages/trackerless-network/protos/NetworkRpc'
8
+ import { INetworkRpc } from '../proto/packages/trackerless-network/protos/NetworkRpc.server'
9
+ import { ServerCallContext } from '@protobuf-ts/runtime-rpc'
10
+
11
+ export interface StreamNodeServerConfig {
12
+ ownPeerDescriptor: PeerDescriptor
13
+ randomGraphId: string
14
+ markAndCheckDuplicate: (messageRef: MessageRef, previousMessageRef?: MessageRef) => boolean
15
+ broadcast: (message: StreamMessage, previousPeer?: string) => void
16
+ onLeaveNotice(notice: LeaveStreamNotice): void
17
+ markForInspection(senderId: PeerIDKey, messageRef: MessageRef): void
18
+ rpcCommunicator: ListeningRpcCommunicator
19
+ }
20
+
21
+ export class StreamNodeServer implements INetworkRpc {
22
+
23
+ private readonly config: StreamNodeServerConfig
24
+
25
+ constructor(config: StreamNodeServerConfig) {
26
+ this.config = config
27
+ }
28
+
29
+ async sendData(message: StreamMessage, context: ServerCallContext): Promise<Empty> {
30
+ const previousPeer = keyFromPeerDescriptor((context as DhtCallContext).incomingSourceDescriptor!)
31
+ this.config.markForInspection(previousPeer, message.messageRef!)
32
+ if (this.config.markAndCheckDuplicate(message.messageRef!, message.previousMessageRef)) {
33
+ this.config.broadcast(message, previousPeer)
34
+ }
35
+ return Empty
36
+ }
37
+
38
+ async leaveStreamNotice(message: LeaveStreamNotice, _context: ServerCallContext): Promise<Empty> {
39
+ if (message.randomGraphId === this.config.randomGraphId) {
40
+ this.config.onLeaveNotice(message)
41
+ }
42
+ return Empty
43
+ }
44
+ }
@@ -0,0 +1,416 @@
1
+ import { RandomGraphNode } from './RandomGraphNode'
2
+ import {
3
+ PeerDescriptor,
4
+ ConnectionLocker,
5
+ DhtNode,
6
+ ITransport,
7
+ keyFromPeerDescriptor
8
+ } from '@streamr/dht'
9
+ import { StreamMessage } from '../proto/packages/trackerless-network/protos/NetworkRpc'
10
+ import { EventEmitter } from 'eventemitter3'
11
+ import {
12
+ Logger,
13
+ MetricsContext,
14
+ RateMetric,
15
+ Metric,
16
+ MetricsDefinition,
17
+ waitForEvent3
18
+ } from '@streamr/utils'
19
+ import { uniq } from 'lodash'
20
+ import { StreamPartID, StreamPartIDUtils } from '@streamr/protocol'
21
+ import { sampleSize } from 'lodash'
22
+ import { StreamEntryPointDiscovery } from './StreamEntryPointDiscovery'
23
+ import { ILayer0 } from './ILayer0'
24
+ import { createRandomGraphNode } from './createRandomGraphNode'
25
+ import { ProxyDirection } from '../proto/packages/trackerless-network/protos/NetworkRpc'
26
+ import { IStreamNode } from './IStreamNode'
27
+ import { ProxyStreamConnectionClient } from './proxy/ProxyStreamConnectionClient'
28
+ import { PeerIDKey } from '@streamr/dht/src/exports'
29
+
30
+ export enum StreamNodeType {
31
+ RANDOM_GRAPH = 'random-graph',
32
+ PROXY = 'proxy'
33
+ }
34
+
35
+ export interface NeighborCounterEvents {
36
+ targetReached: () => void
37
+ }
38
+
39
+ class NeighborCounter {
40
+
41
+ private counter = 0
42
+ private readonly emitter = new EventEmitter<NeighborCounterEvents>()
43
+ private readonly randomGraphNode: RandomGraphNode
44
+ private readonly targetNumberOfNeighbors: number
45
+
46
+ constructor(randomGraphNode: RandomGraphNode, targetNumberOfNeighbors: number) {
47
+ this.randomGraphNode = randomGraphNode
48
+ this.targetNumberOfNeighbors = targetNumberOfNeighbors
49
+ this.counter = randomGraphNode.getTargetNeighborStringIds().length
50
+ this.randomGraphNode.on('targetNeighborConnected', this.onTargetNeighborConnected)
51
+ }
52
+
53
+ private onTargetNeighborConnected = () => {
54
+ this.counter++
55
+ if (this.counter == this.targetNumberOfNeighbors) {
56
+ this.randomGraphNode.off('targetNeighborConnected', this.onTargetNeighborConnected)
57
+ this.emitter.emit('targetReached')
58
+ }
59
+ }
60
+
61
+ public async waitForTargetReached(timeout = 5000): Promise<void> {
62
+ if (this.counter >= this.targetNumberOfNeighbors) {
63
+ return
64
+ } else {
65
+ await waitForEvent3<NeighborCounterEvents>(this.emitter, 'targetReached', timeout)
66
+ }
67
+ }
68
+ }
69
+
70
+ export interface StreamObject {
71
+ layer1?: DhtNode
72
+ layer2: IStreamNode
73
+ type: StreamNodeType
74
+ }
75
+
76
+ export interface Events {
77
+ newMessage: (msg: StreamMessage) => void
78
+ }
79
+
80
+ const logger = new Logger(module)
81
+
82
+ let cleanUp: () => Promise<void> = async () => { }
83
+
84
+ interface Metrics extends MetricsDefinition {
85
+ publishMessagesPerSecond: Metric
86
+ publishBytesPerSecond: Metric
87
+ }
88
+
89
+ export interface StreamrNodeConfig {
90
+ metricsContext?: MetricsContext
91
+ id?: string
92
+ streamPartitionNumOfNeighbors?: number
93
+ streamPartitionMinPropagationTargets?: number
94
+ nodeName?: string
95
+ firstConnectionTimeout?: number
96
+ acceptProxyConnections?: boolean
97
+ }
98
+
99
+ export class StreamrNode extends EventEmitter<Events> {
100
+ private P2PTransport?: ITransport
101
+ private connectionLocker?: ConnectionLocker
102
+ private layer0?: ILayer0
103
+ private streamEntryPointDiscovery?: StreamEntryPointDiscovery
104
+ private readonly metricsContext: MetricsContext
105
+ private readonly metrics: Metrics
106
+ public config: StreamrNodeConfig
107
+ private readonly streams: Map<string, StreamObject>
108
+ private readonly knownStreamEntryPoints: Map<string, PeerDescriptor[]> = new Map()
109
+ protected extraMetadata: Record<string, unknown> = {}
110
+ private started = false
111
+ private destroyed = false
112
+
113
+ constructor(config: StreamrNodeConfig) {
114
+ super()
115
+ this.config = config
116
+ this.streams = new Map()
117
+ this.metricsContext = config.metricsContext ?? new MetricsContext()
118
+ this.metrics = {
119
+ publishMessagesPerSecond: new RateMetric(),
120
+ publishBytesPerSecond: new RateMetric()
121
+ }
122
+ this.metricsContext.addMetrics('node', this.metrics)
123
+ }
124
+
125
+ async start(startedAndJoinedLayer0: ILayer0, transport: ITransport, connectionLocker: ConnectionLocker): Promise<void> {
126
+ if (this.started || this.destroyed) {
127
+ return
128
+ }
129
+ logger.info(`Starting new StreamrNode with id ${keyFromPeerDescriptor(startedAndJoinedLayer0.getPeerDescriptor())}`)
130
+ this.started = true
131
+ this.layer0 = startedAndJoinedLayer0
132
+ this.P2PTransport = transport
133
+ this.connectionLocker = connectionLocker
134
+ this.streamEntryPointDiscovery = new StreamEntryPointDiscovery({
135
+ ownPeerDescriptor: this.getPeerDescriptor(),
136
+ streams: this.streams,
137
+ getEntryPointData: (key) => this.layer0!.getDataFromDht(key),
138
+ getEntryPointDataViaPeer: (peerDescriptor, key) => this.layer0!.findDataViaPeer(peerDescriptor, key),
139
+ storeEntryPointData: (key, data) => this.layer0!.storeDataToDht(key, data),
140
+ deleteEntryPointData: (key) => this.layer0!.deleteDataFromDht(key)
141
+ })
142
+ cleanUp = () => this.destroy()
143
+ }
144
+
145
+ async destroy(): Promise<void> {
146
+ if (!this.started || this.destroyed) {
147
+ return
148
+ }
149
+ logger.trace('Destroying StreamrNode...')
150
+ this.destroyed = true
151
+ this.streams.forEach((stream) => {
152
+ stream.layer2.stop()
153
+ stream.layer1?.stop()
154
+ })
155
+ await this.streamEntryPointDiscovery!.destroy()
156
+ this.streams.clear()
157
+ this.removeAllListeners()
158
+ await this.layer0!.stop()
159
+ await this.P2PTransport!.stop()
160
+ this.layer0 = undefined
161
+ this.P2PTransport = undefined
162
+ this.streamEntryPointDiscovery = undefined
163
+ this.connectionLocker = undefined
164
+ }
165
+
166
+ subscribeToStream(streamPartID: string): void {
167
+ if (!this.streams.has(streamPartID)) {
168
+ this.joinStream(streamPartID)
169
+ .catch((err) => {
170
+ logger.warn(`Failed to subscribe to stream ${streamPartID} with error: ${err}`)
171
+ })
172
+ }
173
+ }
174
+
175
+ publishToStream(streamPartID: string, msg: StreamMessage): void {
176
+ if (this.streams.has(streamPartID)) {
177
+ this.streams.get(streamPartID)!.layer2.broadcast(msg)
178
+ } else {
179
+ this.joinStream(streamPartID)
180
+ .catch((err) => {
181
+ logger.warn(`Failed to publish to stream ${streamPartID} with error: ${err}`)
182
+ })
183
+ this.streams.get(streamPartID)!.layer2.broadcast(msg)
184
+ }
185
+ this.metrics.publishMessagesPerSecond.record(1)
186
+ this.metrics.publishBytesPerSecond.record(msg.content.length)
187
+ }
188
+
189
+ unsubscribeFromStream(streamPartID: string): void {
190
+ this.leaveStream(streamPartID)
191
+ }
192
+
193
+ leaveStream(streamPartID: string): void {
194
+ const stream = this.streams.get(streamPartID)
195
+ if (stream) {
196
+ stream.layer2.stop()
197
+ stream.layer1?.stop()
198
+ this.streams.delete(streamPartID)
199
+ }
200
+ this.streamEntryPointDiscovery!.removeSelfAsEntryPoint(streamPartID)
201
+ }
202
+
203
+ async joinStream(streamPartId: string): Promise<void> {
204
+ if (this.streams.has(streamPartId)) {
205
+ return
206
+ }
207
+ logger.debug(`Joining stream ${streamPartId}`)
208
+ const knownEntryPoints = this.knownStreamEntryPoints.get(streamPartId) ?? []
209
+ let entryPoints = knownEntryPoints.concat(knownEntryPoints)
210
+ const [layer1, layer2] = this.createStream(streamPartId, knownEntryPoints)
211
+ await layer1.start()
212
+ await layer2.start()
213
+ const forwardingPeer = this.layer0!.isJoinOngoing() ? this.layer0!.getKnownEntryPoints()[0] : undefined
214
+ const discoveryResult = await this.streamEntryPointDiscovery!.discoverEntryPointsFromDht(
215
+ streamPartId,
216
+ knownEntryPoints.length,
217
+ forwardingPeer
218
+ )
219
+ entryPoints = knownEntryPoints.concat(discoveryResult.discoveredEntryPoints)
220
+ await layer1.joinDht(sampleSize(entryPoints, 4))
221
+ await this.streamEntryPointDiscovery!.storeSelfAsEntryPointIfNecessary(
222
+ streamPartId,
223
+ discoveryResult.joiningEmptyStream,
224
+ discoveryResult.entryPointsFromDht,
225
+ entryPoints.length
226
+ )
227
+ }
228
+
229
+ private createStream(streamPartID: string, entryPoints: PeerDescriptor[]): [DhtNode, RandomGraphNode] {
230
+ const layer1 = this.createLayer1Node(streamPartID, entryPoints)
231
+ const layer2 = this.createRandomGraphNode(streamPartID, layer1)
232
+ this.streams.set(streamPartID, {
233
+ type: StreamNodeType.RANDOM_GRAPH,
234
+ layer1,
235
+ layer2
236
+ })
237
+ layer2.on('message', (message: StreamMessage) => {
238
+ this.emit('newMessage', message)
239
+ })
240
+ return [layer1, layer2]
241
+ }
242
+
243
+ private createLayer1Node = (streamPartID: string, entryPoints: PeerDescriptor[]) => {
244
+ return new DhtNode({
245
+ transportLayer: this.layer0!,
246
+ serviceId: 'layer1::' + streamPartID,
247
+ peerDescriptor: this.layer0!.getPeerDescriptor(),
248
+ entryPoints: entryPoints,
249
+ numberOfNodesPerKBucket: 4,
250
+ rpcRequestTimeout: 15000,
251
+ dhtJoinTimeout: 60000,
252
+ nodeName: this.config.nodeName + ':layer1'
253
+ })
254
+ }
255
+
256
+ private createRandomGraphNode = (streamPartID: string, layer1: DhtNode) => {
257
+ return createRandomGraphNode({
258
+ randomGraphId: streamPartID,
259
+ P2PTransport: this.P2PTransport!,
260
+ layer1: layer1,
261
+ connectionLocker: this.connectionLocker!,
262
+ ownPeerDescriptor: this.layer0!.getPeerDescriptor(),
263
+ minPropagationTargets: this.config.streamPartitionMinPropagationTargets,
264
+ numOfTargetNeighbors: this.config.streamPartitionNumOfNeighbors,
265
+ name: this.config.nodeName,
266
+ acceptProxyConnections: this.config.acceptProxyConnections
267
+ })
268
+ }
269
+
270
+ async waitForJoinAndPublish(
271
+ streamPartId: string,
272
+ msg: StreamMessage,
273
+ timeout?: number
274
+ ): Promise<number> {
275
+ if (this.getStream(streamPartId)?.type === StreamNodeType.PROXY) {
276
+ return 0
277
+ }
278
+ await this.joinStream(streamPartId)
279
+ if (this.getStream(streamPartId)!.layer1!.getBucketSize() > 0) {
280
+ const neighborCounter = new NeighborCounter(this.getStream(streamPartId)!.layer2 as RandomGraphNode, 1)
281
+ await neighborCounter.waitForTargetReached(timeout)
282
+ }
283
+ this.publishToStream(streamPartId, msg)
284
+ return this.getStream(streamPartId)?.layer2.getTargetNeighborStringIds().length ?? 0
285
+ }
286
+
287
+ async waitForJoinAndSubscribe(
288
+ streamPartId: string,
289
+ timeout?: number,
290
+ expectedNeighbors = 1
291
+ ): Promise<number> {
292
+ if (this.getStream(streamPartId)?.type === StreamNodeType.PROXY) {
293
+ return 0
294
+ }
295
+ await this.joinStream(streamPartId)
296
+ if (this.getStream(streamPartId)!.layer1!.getBucketSize() > 0) {
297
+ const neighborCounter = new NeighborCounter(this.getStream(streamPartId)!.layer2 as RandomGraphNode, expectedNeighbors)
298
+ await neighborCounter.waitForTargetReached(timeout)
299
+ }
300
+ this.subscribeToStream(streamPartId)
301
+ return this.getStream(streamPartId)?.layer2.getTargetNeighborStringIds().length ?? 0
302
+ }
303
+
304
+ async setProxies(
305
+ streamPartId: string,
306
+ contactPeerDescriptors: PeerDescriptor[],
307
+ direction: ProxyDirection,
308
+ getUserId: () => Promise<string>,
309
+ connectionCount?: number
310
+ ): Promise<void> {
311
+ const userId = await getUserId()
312
+ if (this.streams.get(streamPartId)?.type === StreamNodeType.PROXY && contactPeerDescriptors.length > 0) {
313
+ const proxyClient = this.streams.get(streamPartId)!.layer2 as ProxyStreamConnectionClient
314
+ await proxyClient.setProxies(streamPartId, contactPeerDescriptors, direction, userId, connectionCount)
315
+ } else if (this.streams.get(streamPartId)?.type === StreamNodeType.PROXY && contactPeerDescriptors.length === 0) {
316
+ this.streams.get(streamPartId)!.layer2.stop()
317
+ this.streams.delete(streamPartId)
318
+ } else {
319
+ const proxyClient = this.createProxyStream(streamPartId, userId)
320
+ await proxyClient.start()
321
+ await proxyClient.setProxies(streamPartId, contactPeerDescriptors, direction, userId, connectionCount)
322
+ }
323
+ }
324
+
325
+ private createProxyStream(streamPartId: string, userId: string): ProxyStreamConnectionClient {
326
+ const layer2 = this.createProxyStreamConnectionClient(streamPartId, userId)
327
+ this.streams.set(streamPartId, {
328
+ type: StreamNodeType.PROXY,
329
+ layer2
330
+ })
331
+ layer2.on('message', (message: StreamMessage) => {
332
+ this.emit('newMessage', message)
333
+ })
334
+ return layer2
335
+ }
336
+
337
+ private createProxyStreamConnectionClient(streamPartId: string, userId: string): ProxyStreamConnectionClient {
338
+ return new ProxyStreamConnectionClient({
339
+ P2PTransport: this.P2PTransport!,
340
+ ownPeerDescriptor: this.layer0!.getPeerDescriptor(),
341
+ streamPartId: streamPartId,
342
+ connectionLocker: this.connectionLocker!,
343
+ nodeName: this.config.nodeName,
344
+ userId: userId
345
+ })
346
+ }
347
+
348
+ async inspect(peerDescriptor: PeerDescriptor, streamPartId: string): Promise<boolean> {
349
+ if (this.streams.get(streamPartId)?.type === StreamNodeType.RANDOM_GRAPH) {
350
+ const streamNode = this.streams.get(streamPartId)!.layer2 as RandomGraphNode
351
+ return streamNode.inspect(peerDescriptor)
352
+ }
353
+ return false
354
+ }
355
+
356
+ setStreamPartEntryPoints(streamPartId: string, entryPoints: PeerDescriptor[]): void {
357
+ this.knownStreamEntryPoints.set(streamPartId, entryPoints)
358
+ }
359
+
360
+ isProxiedStreamPart(streamId: string, direction: ProxyDirection): boolean {
361
+ return this.streams.get(streamId)?.type === StreamNodeType.PROXY
362
+ && (this.streams.get(streamId)!.layer2 as ProxyStreamConnectionClient).getDirection() === direction
363
+ }
364
+
365
+ hasProxyConnection(streamId: string, peerKey: PeerIDKey, direction: ProxyDirection): boolean {
366
+ return this.streams.has(streamId) && this.streams.get(streamId)!.layer2.hasProxyConnection(peerKey, direction)
367
+ }
368
+
369
+ getStream(streamPartId: string): StreamObject | undefined {
370
+ return this.streams.get(streamPartId)
371
+ }
372
+
373
+ hasStream(streamPartId: string): boolean {
374
+ return this.streams.has(streamPartId)
375
+ }
376
+
377
+ getPeerDescriptor(): PeerDescriptor {
378
+ return this.layer0!.getPeerDescriptor()
379
+ }
380
+
381
+ getNodeId(): string {
382
+ return this.layer0!.getNodeId().toKey()
383
+ }
384
+
385
+ getNodeStringId(): string {
386
+ return this.layer0!.getNodeId().toString()
387
+ }
388
+
389
+ getNeighbors(): string[] {
390
+ const neighbors: string[] = []
391
+ this.streams.forEach((stream) =>
392
+ stream.layer2.getTargetNeighborStringIds().forEach((neighbor) => neighbors.push(neighbor))
393
+ )
394
+ return uniq(neighbors)
395
+ }
396
+
397
+ getStreamParts(): StreamPartID[] {
398
+ return Array.from(this.streams.keys()).map((stringId) => StreamPartIDUtils.parse(stringId))
399
+ }
400
+
401
+ setExtraMetadata(metadata: Record<string, unknown>): void {
402
+ this.extraMetadata = metadata
403
+ }
404
+
405
+ isJoinRequired(streamPartId: StreamPartID): boolean {
406
+ return !this.streams.has(streamPartId) && Array.from(this.streams.values()).every((stream) => stream.type === StreamNodeType.PROXY)
407
+ }
408
+
409
+ }
410
+
411
+ [`exit`, `SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `unhandledRejection`, `SIGTERM`].forEach((term) => {
412
+ process.on(term, async () => {
413
+ await cleanUp()
414
+ process.exit()
415
+ })
416
+ })