@streamr/trackerless-network 100.2.5-beta.1 → 101.0.0-beta.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 (150) hide show
  1. package/dist/package.json +6 -6
  2. package/dist/src/NetworkNode.js +1 -1
  3. package/dist/src/NetworkNode.js.map +1 -1
  4. package/dist/src/NetworkStack.d.ts +5 -5
  5. package/dist/src/NetworkStack.js +21 -21
  6. package/dist/src/NetworkStack.js.map +1 -1
  7. package/dist/src/exports.d.ts +2 -2
  8. package/dist/src/exports.js +2 -2
  9. package/dist/src/exports.js.map +1 -1
  10. package/dist/src/logic/ContentDeliveryLayerNode.d.ts +5 -5
  11. package/dist/src/logic/ContentDeliveryLayerNode.js +85 -85
  12. package/dist/src/logic/ContentDeliveryLayerNode.js.map +1 -1
  13. package/dist/src/logic/ContentDeliveryManager.d.ts +11 -12
  14. package/dist/src/logic/ContentDeliveryManager.js +65 -61
  15. package/dist/src/logic/ContentDeliveryManager.js.map +1 -1
  16. package/dist/src/logic/ContentDeliveryRpcLocal.d.ts +5 -5
  17. package/dist/src/logic/ContentDeliveryRpcLocal.js +9 -9
  18. package/dist/src/logic/ContentDeliveryRpcLocal.js.map +1 -1
  19. package/dist/src/logic/{Layer0Node.d.ts → ControlLayerNode.d.ts} +1 -1
  20. package/dist/src/logic/{Layer0Node.js → ControlLayerNode.js} +1 -1
  21. package/dist/src/logic/ControlLayerNode.js.map +1 -0
  22. package/dist/src/logic/{Layer1Node.d.ts → DiscoveryLayerNode.d.ts} +8 -8
  23. package/dist/src/logic/{Layer1Node.js → DiscoveryLayerNode.js} +1 -1
  24. package/dist/src/logic/DiscoveryLayerNode.js.map +1 -0
  25. package/dist/src/logic/PeerDescriptorStoreManager.d.ts +28 -0
  26. package/dist/src/logic/PeerDescriptorStoreManager.js +79 -0
  27. package/dist/src/logic/PeerDescriptorStoreManager.js.map +1 -0
  28. package/dist/src/logic/StreamPartNetworkSplitAvoidance.d.ts +5 -5
  29. package/dist/src/logic/StreamPartNetworkSplitAvoidance.js +8 -8
  30. package/dist/src/logic/StreamPartNetworkSplitAvoidance.js.map +1 -1
  31. package/dist/src/logic/StreamPartReconnect.d.ts +5 -5
  32. package/dist/src/logic/StreamPartReconnect.js +10 -10
  33. package/dist/src/logic/StreamPartReconnect.js.map +1 -1
  34. package/dist/src/logic/createContentDeliveryLayerNode.d.ts +3 -3
  35. package/dist/src/logic/createContentDeliveryLayerNode.js +34 -34
  36. package/dist/src/logic/createContentDeliveryLayerNode.js.map +1 -1
  37. package/dist/src/logic/inspect/InspectSession.d.ts +3 -3
  38. package/dist/src/logic/inspect/InspectSession.js +5 -5
  39. package/dist/src/logic/inspect/InspectSession.js.map +1 -1
  40. package/dist/src/logic/inspect/Inspector.d.ts +2 -2
  41. package/dist/src/logic/inspect/Inspector.js +8 -8
  42. package/dist/src/logic/inspect/Inspector.js.map +1 -1
  43. package/dist/src/logic/neighbor-discovery/HandshakeRpcLocal.d.ts +4 -4
  44. package/dist/src/logic/neighbor-discovery/HandshakeRpcLocal.js +24 -24
  45. package/dist/src/logic/neighbor-discovery/HandshakeRpcLocal.js.map +1 -1
  46. package/dist/src/logic/neighbor-discovery/HandshakeRpcRemote.d.ts +1 -1
  47. package/dist/src/logic/neighbor-discovery/HandshakeRpcRemote.js +4 -4
  48. package/dist/src/logic/neighbor-discovery/HandshakeRpcRemote.js.map +1 -1
  49. package/dist/src/logic/neighbor-discovery/Handshaker.d.ts +3 -3
  50. package/dist/src/logic/neighbor-discovery/Handshaker.js +37 -37
  51. package/dist/src/logic/neighbor-discovery/Handshaker.js.map +1 -1
  52. package/dist/src/logic/neighbor-discovery/NeighborFinder.d.ts +3 -3
  53. package/dist/src/logic/neighbor-discovery/NeighborFinder.js +9 -9
  54. package/dist/src/logic/neighbor-discovery/NeighborFinder.js.map +1 -1
  55. package/dist/src/logic/neighbor-discovery/NeighborUpdateManager.d.ts +3 -3
  56. package/dist/src/logic/neighbor-discovery/NeighborUpdateManager.js +12 -12
  57. package/dist/src/logic/neighbor-discovery/NeighborUpdateManager.js.map +1 -1
  58. package/dist/src/logic/neighbor-discovery/NeighborUpdateRpcLocal.d.ts +3 -3
  59. package/dist/src/logic/neighbor-discovery/NeighborUpdateRpcLocal.js +14 -14
  60. package/dist/src/logic/neighbor-discovery/NeighborUpdateRpcLocal.js.map +1 -1
  61. package/dist/src/logic/proxy/ProxyClient.d.ts +3 -3
  62. package/dist/src/logic/proxy/ProxyClient.js +25 -25
  63. package/dist/src/logic/proxy/ProxyClient.js.map +1 -1
  64. package/dist/src/logic/proxy/ProxyConnectionRpcLocal.d.ts +3 -3
  65. package/dist/src/logic/proxy/ProxyConnectionRpcLocal.js +10 -10
  66. package/dist/src/logic/proxy/ProxyConnectionRpcLocal.js.map +1 -1
  67. package/dist/src/logic/temporary-connection/TemporaryConnectionRpcLocal.d.ts +3 -3
  68. package/dist/src/logic/temporary-connection/TemporaryConnectionRpcLocal.js +11 -11
  69. package/dist/src/logic/temporary-connection/TemporaryConnectionRpcLocal.js.map +1 -1
  70. package/dist/src/proto/packages/dht/protos/DhtRpc.client.d.ts +4 -4
  71. package/dist/src/proto/packages/dht/protos/DhtRpc.client.js +8 -8
  72. package/dist/src/proto/packages/dht/protos/DhtRpc.client.js.map +1 -1
  73. package/dist/src/proto/packages/dht/protos/DhtRpc.d.ts +11 -3
  74. package/dist/src/proto/packages/dht/protos/DhtRpc.js +7 -5
  75. package/dist/src/proto/packages/dht/protos/DhtRpc.js.map +1 -1
  76. package/dist/src/proto/packages/dht/protos/DhtRpc.server.d.ts +2 -2
  77. package/dist/src/proto/packages/proto-rpc/protos/ProtoRpc.js +1 -1
  78. package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.client.js +1 -1
  79. package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.d.ts +6 -23
  80. package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.js +5 -23
  81. package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.js.map +1 -1
  82. package/dist/test/benchmark/first-message.js +7 -7
  83. package/dist/test/benchmark/first-message.js.map +1 -1
  84. package/dist/test/utils/utils.d.ts +2 -2
  85. package/dist/test/utils/utils.js +3 -3
  86. package/dist/test/utils/utils.js.map +1 -1
  87. package/package.json +6 -6
  88. package/protos/NetworkRpc.proto +3 -9
  89. package/src/NetworkNode.ts +1 -1
  90. package/src/NetworkStack.ts +24 -24
  91. package/src/exports.ts +2 -2
  92. package/src/logic/ContentDeliveryLayerNode.ts +112 -112
  93. package/src/logic/ContentDeliveryManager.ts +71 -67
  94. package/src/logic/ContentDeliveryRpcLocal.ts +12 -12
  95. package/src/logic/{Layer0Node.ts → ControlLayerNode.ts} +1 -1
  96. package/src/logic/{Layer1Node.ts → DiscoveryLayerNode.ts} +8 -8
  97. package/src/logic/PeerDescriptorStoreManager.ts +97 -0
  98. package/src/logic/StreamPartNetworkSplitAvoidance.ts +11 -11
  99. package/src/logic/StreamPartReconnect.ts +12 -12
  100. package/src/logic/createContentDeliveryLayerNode.ts +38 -38
  101. package/src/logic/inspect/InspectSession.ts +6 -6
  102. package/src/logic/inspect/Inspector.ts +9 -9
  103. package/src/logic/neighbor-discovery/HandshakeRpcLocal.ts +26 -26
  104. package/src/logic/neighbor-discovery/HandshakeRpcRemote.ts +6 -6
  105. package/src/logic/neighbor-discovery/Handshaker.ts +45 -45
  106. package/src/logic/neighbor-discovery/NeighborFinder.ts +10 -10
  107. package/src/logic/neighbor-discovery/NeighborUpdateManager.ts +14 -14
  108. package/src/logic/neighbor-discovery/NeighborUpdateRpcLocal.ts +17 -17
  109. package/src/logic/proxy/ProxyClient.ts +26 -26
  110. package/src/logic/proxy/ProxyConnectionRpcLocal.ts +12 -12
  111. package/src/logic/temporary-connection/TemporaryConnectionRpcLocal.ts +13 -13
  112. package/src/proto/google/protobuf/any.ts +1 -1
  113. package/src/proto/google/protobuf/empty.ts +1 -1
  114. package/src/proto/google/protobuf/timestamp.ts +1 -1
  115. package/src/proto/packages/dht/protos/DhtRpc.client.ts +9 -9
  116. package/src/proto/packages/dht/protos/DhtRpc.server.ts +3 -3
  117. package/src/proto/packages/dht/protos/DhtRpc.ts +15 -5
  118. package/src/proto/packages/proto-rpc/protos/ProtoRpc.ts +1 -1
  119. package/src/proto/packages/trackerless-network/protos/NetworkRpc.client.ts +1 -1
  120. package/src/proto/packages/trackerless-network/protos/NetworkRpc.server.ts +1 -1
  121. package/src/proto/packages/trackerless-network/protos/NetworkRpc.ts +10 -27
  122. package/test/benchmark/StreamPartIdDataKeyDistribution.test.ts +1 -1
  123. package/test/benchmark/first-message.ts +9 -9
  124. package/test/end-to-end/content-delivery-layer-node-with-real-connections.test.ts +12 -12
  125. package/test/end-to-end/proxy-and-full-node.test.ts +4 -4
  126. package/test/integration/ContentDeliveryLayerNode-Layer1Node-Latencies.test.ts +19 -19
  127. package/test/integration/ContentDeliveryLayerNode-Layer1Node.test.ts +19 -19
  128. package/test/integration/ContentDeliveryManager.test.ts +11 -11
  129. package/test/integration/Inspect.test.ts +1 -1
  130. package/test/integration/Propagation.test.ts +6 -6
  131. package/test/integration/joining-streams-on-offline-peers.test.ts +3 -3
  132. package/test/integration/stream-without-default-entrypoints.test.ts +2 -2
  133. package/test/integration/streamEntryPointReplacing.test.ts +2 -2
  134. package/test/unit/ContentDeliveryLayerNode.test.ts +11 -11
  135. package/test/unit/ContentDeliveryManager.test.ts +2 -2
  136. package/test/unit/{EntrypointDiscovery.test.ts → PeerDescriptorStoreManager.test.ts} +28 -29
  137. package/test/unit/StreamPartIDDataKey.test.ts +1 -1
  138. package/test/unit/StreamPartNetworkSplitAvoidance.test.ts +8 -8
  139. package/test/unit/StreamPartReconnect.test.ts +9 -9
  140. package/test/utils/fake/FakePeerDescriptorStoreManager.ts +29 -0
  141. package/test/utils/mock/{MockLayer0Node.ts → MockControlLayerNode.ts} +2 -2
  142. package/test/utils/mock/{MockLayer1Node.ts → MockDiscoveryLayerNode.ts} +2 -2
  143. package/test/utils/utils.ts +5 -5
  144. package/dist/src/logic/EntryPointDiscovery.d.ts +0 -27
  145. package/dist/src/logic/EntryPointDiscovery.js +0 -80
  146. package/dist/src/logic/EntryPointDiscovery.js.map +0 -1
  147. package/dist/src/logic/Layer0Node.js.map +0 -1
  148. package/dist/src/logic/Layer1Node.js.map +0 -1
  149. package/src/logic/EntryPointDiscovery.ts +0 -100
  150. package/test/utils/fake/FakeEntryPointDiscovery.ts +0 -29
@@ -5,6 +5,7 @@ import {
5
5
  EXISTING_CONNECTION_TIMEOUT,
6
6
  ITransport,
7
7
  PeerDescriptor,
8
+ getDhtAddressFromRaw,
8
9
  getNodeIdFromPeerDescriptor
9
10
  } from '@streamr/dht'
10
11
  import { StreamID, StreamPartID, StreamPartIDUtils, toStreamPartID } from '@streamr/protocol'
@@ -16,26 +17,26 @@ import {
16
17
  MetricsDefinition,
17
18
  RateMetric
18
19
  } from '@streamr/utils'
20
+ import { createHash } from 'crypto'
19
21
  import { EventEmitter } from 'eventemitter3'
20
22
  import { sampleSize } from 'lodash'
21
23
  import { ProxyDirection, StreamMessage, StreamPartitionInfo } from '../proto/packages/trackerless-network/protos/NetworkRpc'
22
- import { ENTRYPOINT_STORE_LIMIT, EntryPointDiscovery } from './EntryPointDiscovery'
23
- import { Layer0Node } from './Layer0Node'
24
- import { Layer1Node } from './Layer1Node'
25
24
  import { ContentDeliveryLayerNode } from './ContentDeliveryLayerNode'
25
+ import { ControlLayerNode } from './ControlLayerNode'
26
+ import { DiscoveryLayerNode } from './DiscoveryLayerNode'
27
+ import { MAX_NODE_COUNT, PeerDescriptorStoreManager } from './PeerDescriptorStoreManager'
28
+ import { MIN_NEIGHBOR_COUNT as NETWORK_SPLIT_AVOIDANCE_MIN_NEIGHBOR_COUNT, StreamPartNetworkSplitAvoidance } from './StreamPartNetworkSplitAvoidance'
29
+ import { StreamPartReconnect } from './StreamPartReconnect'
26
30
  import { createContentDeliveryLayerNode } from './createContentDeliveryLayerNode'
27
31
  import { ProxyClient } from './proxy/ProxyClient'
28
- import { StreamPartReconnect } from './StreamPartReconnect'
29
- import { MIN_NEIGHBOR_COUNT as NETWORK_SPLIT_AVOIDANCE_MIN_NEIGHBOR_COUNT, StreamPartNetworkSplitAvoidance } from './StreamPartNetworkSplitAvoidance'
30
32
 
31
33
  export type StreamPartDelivery = {
32
34
  broadcast: (msg: StreamMessage) => void
33
35
  stop: () => Promise<void>
34
36
  } & ({
35
37
  proxied: false
36
- layer1Node: Layer1Node
38
+ discoveryLayerNode: DiscoveryLayerNode
37
39
  node: ContentDeliveryLayerNode
38
- entryPointDiscovery: EntryPointDiscovery
39
40
  networkSplitAvoidance: StreamPartNetworkSplitAvoidance
40
41
  } | {
41
42
  proxied: true
@@ -53,7 +54,7 @@ interface Metrics extends MetricsDefinition {
53
54
  broadcastBytesPerSecond: Metric
54
55
  }
55
56
 
56
- export interface ContentDeliveryManagerConfig {
57
+ export interface ContentDeliveryManagerOptions {
57
58
  metricsContext?: MetricsContext
58
59
  streamPartitionNeighborTargetCount?: number
59
60
  streamPartitionMinPropagationTargets?: number
@@ -61,24 +62,28 @@ export interface ContentDeliveryManagerConfig {
61
62
  rpcRequestTimeout?: number
62
63
  }
63
64
 
65
+ export const streamPartIdToDataKey = (streamPartId: StreamPartID): DhtAddress => {
66
+ return getDhtAddressFromRaw(new Uint8Array((createHash('sha1').update(streamPartId).digest())))
67
+ }
68
+
64
69
  export class ContentDeliveryManager extends EventEmitter<Events> {
65
70
 
66
71
  private transport?: ITransport
67
72
  private connectionLocker?: ConnectionLocker
68
- private layer0Node?: Layer0Node
73
+ private controlLayerNode?: ControlLayerNode
69
74
  private readonly metricsContext: MetricsContext
70
75
  private readonly metrics: Metrics
71
- private readonly config: ContentDeliveryManagerConfig
76
+ private readonly options: ContentDeliveryManagerOptions
72
77
  private readonly streamParts: Map<StreamPartID, StreamPartDelivery>
73
78
  private readonly knownStreamPartEntryPoints: Map<StreamPartID, PeerDescriptor[]> = new Map()
74
79
  private started = false
75
80
  private destroyed = false
76
81
 
77
- constructor(config: ContentDeliveryManagerConfig) {
82
+ constructor(options: ContentDeliveryManagerOptions) {
78
83
  super()
79
- this.config = config
84
+ this.options = options
80
85
  this.streamParts = new Map()
81
- this.metricsContext = config.metricsContext ?? new MetricsContext()
86
+ this.metricsContext = options.metricsContext ?? new MetricsContext()
82
87
  this.metrics = {
83
88
  broadcastMessagesPerSecond: new RateMetric(),
84
89
  broadcastBytesPerSecond: new RateMetric()
@@ -86,12 +91,12 @@ export class ContentDeliveryManager extends EventEmitter<Events> {
86
91
  this.metricsContext.addMetrics('node', this.metrics)
87
92
  }
88
93
 
89
- async start(startedAndJoinedLayer0Node: Layer0Node, transport: ITransport, connectionLocker: ConnectionLocker): Promise<void> {
94
+ async start(startedAndJoinedControlLayerNode: ControlLayerNode, transport: ITransport, connectionLocker: ConnectionLocker): Promise<void> {
90
95
  if (this.started || this.destroyed) {
91
96
  return
92
97
  }
93
98
  this.started = true
94
- this.layer0Node = startedAndJoinedLayer0Node
99
+ this.controlLayerNode = startedAndJoinedControlLayerNode
95
100
  this.transport = transport
96
101
  this.connectionLocker = connectionLocker
97
102
  }
@@ -105,7 +110,7 @@ export class ContentDeliveryManager extends EventEmitter<Events> {
105
110
  await Promise.all(Array.from(this.streamParts.values()).map((streamPart) => streamPart.stop()))
106
111
  this.streamParts.clear()
107
112
  this.removeAllListeners()
108
- this.layer0Node = undefined
113
+ this.controlLayerNode = undefined
109
114
  this.transport = undefined
110
115
  this.connectionLocker = undefined
111
116
  }
@@ -135,37 +140,36 @@ export class ContentDeliveryManager extends EventEmitter<Events> {
135
140
  return
136
141
  }
137
142
  logger.debug(`Join stream part ${streamPartId}`)
138
- const layer1Node = this.createLayer1Node(streamPartId, this.knownStreamPartEntryPoints.get(streamPartId) ?? [])
139
- const entryPointDiscovery = new EntryPointDiscovery({
140
- streamPartId,
143
+ const discoveryLayerNode = this.createDiscoveryLayerNode(streamPartId, this.knownStreamPartEntryPoints.get(streamPartId) ?? [])
144
+ const peerDescriptorStoreManager = new PeerDescriptorStoreManager({
145
+ key: streamPartIdToDataKey(streamPartId),
141
146
  localPeerDescriptor: this.getPeerDescriptor(),
142
- fetchEntryPointData: (key) => this.layer0Node!.fetchDataFromDht(key),
143
- storeEntryPointData: (key, data) => this.layer0Node!.storeDataToDht(key, data),
144
- deleteEntryPointData: async (key) => this.layer0Node!.deleteDataFromDht(key, false)
147
+ fetchDataFromDht: (key) => this.controlLayerNode!.fetchDataFromDht(key),
148
+ storeDataToDht: (key, data) => this.controlLayerNode!.storeDataToDht(key, data),
149
+ deleteDataFromDht: async (key, waitForCompletion) => this.controlLayerNode!.deleteDataFromDht(key, waitForCompletion)
145
150
  })
146
151
  const networkSplitAvoidance = new StreamPartNetworkSplitAvoidance({
147
- layer1Node,
148
- discoverEntryPoints: async () => entryPointDiscovery.discoverEntryPoints()
152
+ discoveryLayerNode,
153
+ discoverEntryPoints: async () => peerDescriptorStoreManager.fetchNodes()
149
154
  })
150
155
  const node = this.createContentDeliveryLayerNode(
151
156
  streamPartId,
152
- layer1Node,
153
- () => entryPointDiscovery.isLocalNodeEntryPoint()
157
+ discoveryLayerNode,
158
+ () => peerDescriptorStoreManager.isLocalNodeStored()
154
159
  )
155
- const streamPartReconnect = new StreamPartReconnect(layer1Node, entryPointDiscovery)
160
+ const streamPartReconnect = new StreamPartReconnect(discoveryLayerNode, peerDescriptorStoreManager)
156
161
  streamPart = {
157
162
  proxied: false,
158
- layer1Node,
163
+ discoveryLayerNode,
159
164
  node,
160
- entryPointDiscovery,
161
165
  networkSplitAvoidance,
162
166
  broadcast: (msg: StreamMessage) => node.broadcast(msg),
163
167
  stop: async () => {
164
168
  streamPartReconnect.destroy()
165
169
  networkSplitAvoidance.destroy()
166
- await entryPointDiscovery.destroy()
170
+ await peerDescriptorStoreManager.destroy()
167
171
  node.stop()
168
- await layer1Node.stop()
172
+ await discoveryLayerNode.stop()
169
173
  }
170
174
  }
171
175
  this.streamParts.set(streamPartId, streamPart)
@@ -173,15 +177,15 @@ export class ContentDeliveryManager extends EventEmitter<Events> {
173
177
  this.emit('newMessage', message)
174
178
  })
175
179
  const handleEntryPointLeave = async () => {
176
- if (this.destroyed || entryPointDiscovery.isLocalNodeEntryPoint() || this.knownStreamPartEntryPoints.has(streamPartId)) {
180
+ if (this.destroyed || peerDescriptorStoreManager.isLocalNodeStored() || this.knownStreamPartEntryPoints.has(streamPartId)) {
177
181
  return
178
182
  }
179
- const entryPoints = await entryPointDiscovery.discoverEntryPoints()
180
- if (entryPoints.length < ENTRYPOINT_STORE_LIMIT) {
181
- await entryPointDiscovery.storeAndKeepLocalNodeAsEntryPoint()
183
+ const entryPoints = await peerDescriptorStoreManager.fetchNodes()
184
+ if (entryPoints.length < MAX_NODE_COUNT) {
185
+ await peerDescriptorStoreManager.storeAndKeepLocalNode()
182
186
  }
183
187
  }
184
- layer1Node.on('manualRejoinRequired', async () => {
188
+ discoveryLayerNode.on('manualRejoinRequired', async () => {
185
189
  if (!streamPartReconnect.isRunning() && !networkSplitAvoidance.isRunning()) {
186
190
  logger.debug('Manual rejoin required for stream part', { streamPartId })
187
191
  await streamPartReconnect.reconnect()
@@ -190,53 +194,53 @@ export class ContentDeliveryManager extends EventEmitter<Events> {
190
194
  node.on('entryPointLeaveDetected', () => handleEntryPointLeave())
191
195
  setImmediate(async () => {
192
196
  try {
193
- await this.startLayersAndJoinDht(streamPartId, entryPointDiscovery)
197
+ await this.startLayersAndJoinDht(streamPartId, peerDescriptorStoreManager)
194
198
  } catch (err) {
195
199
  logger.warn(`Failed to join to stream part ${streamPartId}`, { err })
196
200
  }
197
201
  })
198
202
  }
199
203
 
200
- private async startLayersAndJoinDht(streamPartId: StreamPartID, entryPointDiscovery: EntryPointDiscovery): Promise<void> {
204
+ private async startLayersAndJoinDht(streamPartId: StreamPartID, peerDescriptorStoreManager: PeerDescriptorStoreManager): Promise<void> {
201
205
  logger.debug(`Start layers and join DHT for stream part ${streamPartId}`)
202
206
  const streamPart = this.streamParts.get(streamPartId)
203
207
  if ((streamPart === undefined) || streamPart.proxied) {
204
208
  // leaveStreamPart has been called (or leaveStreamPart called, and then setProxies called)
205
209
  return
206
210
  }
207
- await streamPart.layer1Node.start()
211
+ await streamPart.discoveryLayerNode.start()
208
212
  await streamPart.node.start()
209
213
  const knownEntryPoints = this.knownStreamPartEntryPoints.get(streamPartId)
210
214
  if (knownEntryPoints !== undefined) {
211
215
  await Promise.all([
212
- streamPart.layer1Node.joinDht(knownEntryPoints),
213
- streamPart.layer1Node.joinRing()
216
+ streamPart.discoveryLayerNode.joinDht(knownEntryPoints),
217
+ streamPart.discoveryLayerNode.joinRing()
214
218
  ])
215
219
  } else {
216
- const entryPoints = await entryPointDiscovery.discoverEntryPoints()
220
+ const entryPoints = await peerDescriptorStoreManager.fetchNodes()
217
221
  await Promise.all([
218
- streamPart.layer1Node.joinDht(sampleSize(entryPoints, NETWORK_SPLIT_AVOIDANCE_MIN_NEIGHBOR_COUNT)),
219
- streamPart.layer1Node.joinRing()
222
+ streamPart.discoveryLayerNode.joinDht(sampleSize(entryPoints, NETWORK_SPLIT_AVOIDANCE_MIN_NEIGHBOR_COUNT)),
223
+ streamPart.discoveryLayerNode.joinRing()
220
224
  ])
221
- if (entryPoints.length < ENTRYPOINT_STORE_LIMIT) {
222
- await entryPointDiscovery.storeAndKeepLocalNodeAsEntryPoint()
223
- if (streamPart.layer1Node.getNeighborCount() < NETWORK_SPLIT_AVOIDANCE_MIN_NEIGHBOR_COUNT) {
225
+ if (entryPoints.length < MAX_NODE_COUNT) {
226
+ await peerDescriptorStoreManager.storeAndKeepLocalNode()
227
+ if (streamPart.discoveryLayerNode.getNeighborCount() < NETWORK_SPLIT_AVOIDANCE_MIN_NEIGHBOR_COUNT) {
224
228
  setImmediate(() => streamPart.networkSplitAvoidance.avoidNetworkSplit())
225
229
  }
226
230
  }
227
231
  }
228
232
  }
229
233
 
230
- private createLayer1Node(streamPartId: StreamPartID, entryPoints: PeerDescriptor[]): Layer1Node {
234
+ private createDiscoveryLayerNode(streamPartId: StreamPartID, entryPoints: PeerDescriptor[]): DiscoveryLayerNode {
231
235
  return new DhtNode({
232
- transport: this.layer0Node!,
233
- connectionsView: this.layer0Node!.getConnectionsView(),
236
+ transport: this.controlLayerNode!,
237
+ connectionsView: this.controlLayerNode!.getConnectionsView(),
234
238
  serviceId: 'layer1::' + streamPartId,
235
- peerDescriptor: this.layer0Node!.getLocalPeerDescriptor(),
239
+ peerDescriptor: this.controlLayerNode!.getLocalPeerDescriptor(),
236
240
  entryPoints,
237
- numberOfNodesPerKBucket: 4, // TODO use config option or named constant?
241
+ numberOfNodesPerKBucket: 4, // TODO use options option or named constant?
238
242
  rpcRequestTimeout: EXISTING_CONNECTION_TIMEOUT,
239
- dhtJoinTimeout: 20000, // TODO use config option or named constant?
243
+ dhtJoinTimeout: 20000, // TODO use options option or named constant?
240
244
  periodicallyPingNeighbors: true,
241
245
  periodicallyPingRingContacts: true
242
246
  })
@@ -244,19 +248,19 @@ export class ContentDeliveryManager extends EventEmitter<Events> {
244
248
 
245
249
  private createContentDeliveryLayerNode(
246
250
  streamPartId: StreamPartID,
247
- layer1Node: Layer1Node,
251
+ discoveryLayerNode: DiscoveryLayerNode,
248
252
  isLocalNodeEntryPoint: () => boolean
249
253
  ) {
250
254
  return createContentDeliveryLayerNode({
251
255
  streamPartId,
252
256
  transport: this.transport!,
253
- layer1Node,
257
+ discoveryLayerNode,
254
258
  connectionLocker: this.connectionLocker!,
255
- localPeerDescriptor: this.layer0Node!.getLocalPeerDescriptor(),
256
- minPropagationTargets: this.config.streamPartitionMinPropagationTargets,
257
- neighborTargetCount: this.config.streamPartitionNeighborTargetCount,
258
- acceptProxyConnections: this.config.acceptProxyConnections,
259
- rpcRequestTimeout: this.config.rpcRequestTimeout,
259
+ localPeerDescriptor: this.controlLayerNode!.getLocalPeerDescriptor(),
260
+ minPropagationTargets: this.options.streamPartitionMinPropagationTargets,
261
+ neighborTargetCount: this.options.streamPartitionNeighborTargetCount,
262
+ acceptProxyConnections: this.options.acceptProxyConnections,
263
+ rpcRequestTimeout: this.options.rpcRequestTimeout,
260
264
  isLocalNodeEntryPoint
261
265
  })
262
266
  }
@@ -269,7 +273,7 @@ export class ContentDeliveryManager extends EventEmitter<Events> {
269
273
  connectionCount?: number
270
274
  ): Promise<void> {
271
275
  // TODO explicit default value for "acceptProxyConnections" or make it required
272
- if (this.config.acceptProxyConnections) {
276
+ if (this.options.acceptProxyConnections) {
273
277
  throw new Error('cannot set proxies when acceptProxyConnections=true')
274
278
  }
275
279
  const enable = (nodes.length > 0) && ((connectionCount === undefined) || (connectionCount > 0))
@@ -301,10 +305,10 @@ export class ContentDeliveryManager extends EventEmitter<Events> {
301
305
  private createProxyClient(streamPartId: StreamPartID): ProxyClient {
302
306
  return new ProxyClient({
303
307
  transport: this.transport!,
304
- localPeerDescriptor: this.layer0Node!.getLocalPeerDescriptor(),
308
+ localPeerDescriptor: this.controlLayerNode!.getLocalPeerDescriptor(),
305
309
  streamPartId,
306
310
  connectionLocker: this.connectionLocker!,
307
- minPropagationTargets: this.config.streamPartitionMinPropagationTargets
311
+ minPropagationTargets: this.options.streamPartitionMinPropagationTargets
308
312
  })
309
313
  }
310
314
 
@@ -320,10 +324,10 @@ export class ContentDeliveryManager extends EventEmitter<Events> {
320
324
  getNodeInfo(): StreamPartitionInfo[] {
321
325
  const streamParts = Array.from(this.streamParts.entries()).filter(([_, node]) => node.proxied === false)
322
326
  return streamParts.map(([streamPartId]) => {
323
- const stream = this.streamParts.get(streamPartId)! as { node: ContentDeliveryLayerNode, layer1Node: Layer1Node }
327
+ const stream = this.streamParts.get(streamPartId)! as { node: ContentDeliveryLayerNode, discoveryLayerNode: DiscoveryLayerNode }
324
328
  return {
325
329
  id: streamPartId,
326
- controlLayerNeighbors: stream.layer1Node.getNeighbors(),
330
+ controlLayerNeighbors: stream.discoveryLayerNode.getNeighbors(),
327
331
  contentDeliveryLayerNeighbors: stream.node.getNeighbors()
328
332
  }
329
333
  })
@@ -350,11 +354,11 @@ export class ContentDeliveryManager extends EventEmitter<Events> {
350
354
  }
351
355
 
352
356
  getPeerDescriptor(): PeerDescriptor {
353
- return this.layer0Node!.getLocalPeerDescriptor()
357
+ return this.controlLayerNode!.getLocalPeerDescriptor()
354
358
  }
355
359
 
356
360
  getNodeId(): DhtAddress {
357
- return getNodeIdFromPeerDescriptor(this.layer0Node!.getLocalPeerDescriptor())
361
+ return getNodeIdFromPeerDescriptor(this.controlLayerNode!.getLocalPeerDescriptor())
358
362
  }
359
363
 
360
364
  getNeighbors(streamPartId: StreamPartID): DhtAddress[] {
@@ -10,38 +10,38 @@ import { IContentDeliveryRpc } from '../proto/packages/trackerless-network/proto
10
10
  import { ServerCallContext } from '@protobuf-ts/runtime-rpc'
11
11
  import { StreamPartID } from '@streamr/protocol'
12
12
 
13
- export interface ContentDeliveryRpcLocalConfig {
13
+ export interface ContentDeliveryRpcLocalOptions {
14
14
  localPeerDescriptor: PeerDescriptor
15
15
  streamPartId: StreamPartID
16
16
  markAndCheckDuplicate: (messageId: MessageID, previousMessageRef?: MessageRef) => boolean
17
17
  broadcast: (message: StreamMessage, previousNode?: DhtAddress) => void
18
- onLeaveNotice(senderId: DhtAddress, isLocalNodeEntryPoint: boolean): void
19
- markForInspection(senderId: DhtAddress, messageId: MessageID): void
18
+ onLeaveNotice(remoteNodeId: DhtAddress, isLocalNodeEntryPoint: boolean): void
19
+ markForInspection(remoteNodeId: DhtAddress, messageId: MessageID): void
20
20
  rpcCommunicator: ListeningRpcCommunicator
21
21
  }
22
22
 
23
23
  export class ContentDeliveryRpcLocal implements IContentDeliveryRpc {
24
24
 
25
- private readonly config: ContentDeliveryRpcLocalConfig
25
+ private readonly options: ContentDeliveryRpcLocalOptions
26
26
 
27
- constructor(config: ContentDeliveryRpcLocalConfig) {
28
- this.config = config
27
+ constructor(options: ContentDeliveryRpcLocalOptions) {
28
+ this.options = options
29
29
  }
30
30
 
31
31
  async sendStreamMessage(message: StreamMessage, context: ServerCallContext): Promise<Empty> {
32
32
  const previousNode = getNodeIdFromPeerDescriptor((context as DhtCallContext).incomingSourceDescriptor!)
33
- this.config.markForInspection(previousNode, message.messageId!)
34
- if (this.config.markAndCheckDuplicate(message.messageId!, message.previousMessageRef)) {
35
- this.config.broadcast(message, previousNode)
33
+ this.options.markForInspection(previousNode, message.messageId!)
34
+ if (this.options.markAndCheckDuplicate(message.messageId!, message.previousMessageRef)) {
35
+ this.options.broadcast(message, previousNode)
36
36
  }
37
37
  return Empty
38
38
  }
39
39
 
40
40
  async leaveStreamPartNotice(message: LeaveStreamPartNotice, context: ServerCallContext): Promise<Empty> {
41
- if (message.streamPartId === this.config.streamPartId) {
41
+ if (message.streamPartId === this.options.streamPartId) {
42
42
  const sourcePeerDescriptor = (context as DhtCallContext).incomingSourceDescriptor!
43
- const sourceId = getNodeIdFromPeerDescriptor(sourcePeerDescriptor)
44
- this.config.onLeaveNotice(sourceId, message.isEntryPoint)
43
+ const remoteNodeId = getNodeIdFromPeerDescriptor(sourcePeerDescriptor)
44
+ this.options.onLeaveNotice(remoteNodeId, message.isEntryPoint)
45
45
  }
46
46
  return Empty
47
47
  }
@@ -1,7 +1,7 @@
1
1
  import { ConnectionsView, DataEntry, DhtAddress, ITransport, PeerDescriptor } from '@streamr/dht'
2
2
  import { Any } from '../proto/google/protobuf/any'
3
3
 
4
- export interface Layer0Node extends ITransport {
4
+ export interface ControlLayerNode extends ITransport {
5
5
  joinDht(entryPointDescriptors: PeerDescriptor[]): Promise<void>
6
6
  hasJoined(): boolean
7
7
  getLocalPeerDescriptor(): PeerDescriptor
@@ -1,6 +1,6 @@
1
1
  import { DhtAddress, PeerDescriptor, RingContacts } from '@streamr/dht'
2
2
 
3
- export interface Layer1NodeEvents {
3
+ export interface DiscoveryLayerNodeEvents {
4
4
  manualRejoinRequired: () => void
5
5
  nearbyContactAdded: (peerDescriptor: PeerDescriptor) => void
6
6
  nearbyContactRemoved: (peerDescriptor: PeerDescriptor) => void
@@ -10,13 +10,13 @@ export interface Layer1NodeEvents {
10
10
  ringContactRemoved: (peerDescriptor: PeerDescriptor) => void
11
11
  }
12
12
 
13
- export interface Layer1Node {
14
- on<T extends keyof Layer1NodeEvents>(eventName: T, listener: (peerDescriptor: PeerDescriptor) => void): void
15
- once<T extends keyof Layer1NodeEvents>(eventName: T, listener: (peerDescriptor: PeerDescriptor) => void): void
16
- off<T extends keyof Layer1NodeEvents>(eventName: T, listener: (peerDescriptor: PeerDescriptor) => void): void
17
- on<T extends keyof Layer1NodeEvents>(eventName: T, listener: () => void): void
18
- once<T extends keyof Layer1NodeEvents>(eventName: T, listener: () => void): void
19
- off<T extends keyof Layer1NodeEvents>(eventName: T, listener: () => void): void
13
+ export interface DiscoveryLayerNode {
14
+ on<T extends keyof DiscoveryLayerNodeEvents>(eventName: T, listener: (peerDescriptor: PeerDescriptor) => void): void
15
+ once<T extends keyof DiscoveryLayerNodeEvents>(eventName: T, listener: (peerDescriptor: PeerDescriptor) => void): void
16
+ off<T extends keyof DiscoveryLayerNodeEvents>(eventName: T, listener: (peerDescriptor: PeerDescriptor) => void): void
17
+ on<T extends keyof DiscoveryLayerNodeEvents>(eventName: T, listener: () => void): void
18
+ once<T extends keyof DiscoveryLayerNodeEvents>(eventName: T, listener: () => void): void
19
+ off<T extends keyof DiscoveryLayerNodeEvents>(eventName: T, listener: () => void): void
20
20
  removeContact: (nodeId: DhtAddress) => void
21
21
  getClosestContacts: (maxCount?: number) => PeerDescriptor[]
22
22
  getRandomContacts: (maxCount?: number) => PeerDescriptor[]
@@ -0,0 +1,97 @@
1
+ import {
2
+ DataEntry,
3
+ DhtAddress,
4
+ PeerDescriptor,
5
+ areEqualPeerDescriptors
6
+ } from '@streamr/dht'
7
+ import { Logger, scheduleAtInterval } from '@streamr/utils'
8
+ import { Any } from '../proto/google/protobuf/any'
9
+
10
+ const parsePeerDescriptor = (dataEntries: DataEntry[]): PeerDescriptor[] => {
11
+ return dataEntries.filter((entry) => !entry.deleted).map((entry) => Any.unpack(entry.data!, PeerDescriptor))
12
+ }
13
+
14
+ const logger = new Logger(module)
15
+
16
+ export const MAX_NODE_COUNT = 8
17
+
18
+ interface PeerDescriptorStoreManagerOptions {
19
+ key: DhtAddress
20
+ localPeerDescriptor: PeerDescriptor
21
+ storeInterval?: number
22
+ fetchDataFromDht: (key: DhtAddress) => Promise<DataEntry[]>
23
+ storeDataToDht: (key: DhtAddress, data: Any) => Promise<PeerDescriptor[]>
24
+ deleteDataFromDht: (key: DhtAddress, waitForCompletion: boolean) => Promise<void>
25
+ }
26
+
27
+ /**
28
+ * For each key there is usually 0-MAX_NODE_COUNT PeerDescriptors stored in the DHT. If there are fewer node,
29
+ * the peer descriptor of the local node is stored to the DHT.
30
+ */
31
+ export class PeerDescriptorStoreManager {
32
+
33
+ private readonly abortController: AbortController
34
+ private readonly options: PeerDescriptorStoreManagerOptions
35
+ // eslint-disable-next-line no-underscore-dangle
36
+ private isLocalNodeStored_ = false
37
+
38
+ constructor(options: PeerDescriptorStoreManagerOptions) {
39
+ this.options = options
40
+ this.abortController = new AbortController()
41
+ }
42
+
43
+ async fetchNodes(): Promise<PeerDescriptor[]> {
44
+ logger.trace('Fetch data', { key: this.options.key })
45
+ try {
46
+ const result = await this.options.fetchDataFromDht(this.options.key)
47
+ return parsePeerDescriptor(result)
48
+ } catch (err) {
49
+ return []
50
+ }
51
+ }
52
+
53
+ async storeAndKeepLocalNode(): Promise<void> {
54
+ if (this.abortController.signal.aborted) {
55
+ return
56
+ }
57
+ // eslint-disable-next-line no-underscore-dangle
58
+ this.isLocalNodeStored_ = true
59
+ await this.storeLocalNode()
60
+ await this.keepLocalNode()
61
+ }
62
+
63
+ private async storeLocalNode(): Promise<void> {
64
+ const localPeerDescriptor = this.options.localPeerDescriptor
65
+ const dataToStore = Any.pack(localPeerDescriptor, PeerDescriptor)
66
+ try {
67
+ await this.options.storeDataToDht(this.options.key, dataToStore)
68
+ } catch (err) {
69
+ logger.warn('Failed to store local node', { key: this.options.key })
70
+ }
71
+ }
72
+
73
+ private async keepLocalNode(): Promise<void> {
74
+ await scheduleAtInterval(async () => {
75
+ logger.trace('Attempting to keep local node', { key: this.options.key })
76
+ try {
77
+ const discovered = await this.fetchNodes()
78
+ if (discovered.length < MAX_NODE_COUNT
79
+ || discovered.some((peerDescriptor) => areEqualPeerDescriptors(peerDescriptor, this.options.localPeerDescriptor))) {
80
+ await this.storeLocalNode()
81
+ }
82
+ } catch (err) {
83
+ logger.debug('Failed to keep local node', { key: this.options.key })
84
+ }
85
+ }, this.options.storeInterval ?? 60000, false, this.abortController.signal)
86
+ }
87
+
88
+ public isLocalNodeStored(): boolean {
89
+ // eslint-disable-next-line no-underscore-dangle
90
+ return this.isLocalNodeStored_
91
+ }
92
+
93
+ async destroy(): Promise<void> {
94
+ this.abortController.abort()
95
+ await this.options.deleteDataFromDht(this.options.key, false)
96
+ }
97
+ }
@@ -1,6 +1,6 @@
1
1
  import { areEqualPeerDescriptors, DhtAddress, getNodeIdFromPeerDescriptor, PeerDescriptor } from '@streamr/dht'
2
2
  import { Logger, wait } from '@streamr/utils'
3
- import { Layer1Node } from './Layer1Node'
3
+ import { DiscoveryLayerNode } from './DiscoveryLayerNode'
4
4
 
5
5
  /*
6
6
  * Tries to find new neighbors if we currently have less than MIN_NEIGHBOR_COUNT neigbors. It does so by
@@ -40,8 +40,8 @@ const exponentialRunOff = async (
40
40
 
41
41
  export const MIN_NEIGHBOR_COUNT = 4
42
42
 
43
- export interface StreamPartNetworkSplitAvoidanceConfig {
44
- layer1Node: Layer1Node
43
+ export interface StreamPartNetworkSplitAvoidanceOptions {
44
+ discoveryLayerNode: DiscoveryLayerNode
45
45
  discoverEntryPoints: (excludedNodes?: Set<DhtAddress>) => Promise<PeerDescriptor[]>
46
46
  exponentialRunOfBaseDelay?: number
47
47
  }
@@ -49,31 +49,31 @@ export interface StreamPartNetworkSplitAvoidanceConfig {
49
49
  export class StreamPartNetworkSplitAvoidance {
50
50
 
51
51
  private readonly abortController: AbortController
52
- private readonly config: StreamPartNetworkSplitAvoidanceConfig
52
+ private readonly options: StreamPartNetworkSplitAvoidanceOptions
53
53
  private readonly excludedNodes: Set<DhtAddress> = new Set()
54
54
  private running = false
55
55
 
56
- constructor(config: StreamPartNetworkSplitAvoidanceConfig) {
57
- this.config = config
56
+ constructor(options: StreamPartNetworkSplitAvoidanceOptions) {
57
+ this.options = options
58
58
  this.abortController = new AbortController()
59
59
  }
60
60
 
61
61
  public async avoidNetworkSplit(): Promise<void> {
62
62
  this.running = true
63
63
  await exponentialRunOff(async () => {
64
- const discoveredEntrypoints = await this.config.discoverEntryPoints()
64
+ const discoveredEntrypoints = await this.options.discoverEntryPoints()
65
65
  const filteredEntryPoints = discoveredEntrypoints.filter((peer) => !this.excludedNodes.has(getNodeIdFromPeerDescriptor(peer)))
66
- await this.config.layer1Node.joinDht(filteredEntryPoints, false, false)
67
- if (this.config.layer1Node.getNeighborCount() < MIN_NEIGHBOR_COUNT) {
66
+ await this.options.discoveryLayerNode.joinDht(filteredEntryPoints, false, false)
67
+ if (this.options.discoveryLayerNode.getNeighborCount() < MIN_NEIGHBOR_COUNT) {
68
68
  // Filter out nodes that are not neighbors as those nodes are assumed to be offline
69
69
  const newExcludes = filteredEntryPoints
70
- .filter((peer) => !this.config.layer1Node.getNeighbors()
70
+ .filter((peer) => !this.options.discoveryLayerNode.getNeighbors()
71
71
  .some((neighbor) => areEqualPeerDescriptors(neighbor, peer)))
72
72
  .map((peer) => getNodeIdFromPeerDescriptor(peer))
73
73
  newExcludes.forEach((node) => this.excludedNodes.add(node))
74
74
  throw new Error(`Network split is still possible`)
75
75
  }
76
- }, 'avoid network split', this.abortController.signal, this.config.exponentialRunOfBaseDelay)
76
+ }, 'avoid network split', this.abortController.signal, this.options.exponentialRunOfBaseDelay)
77
77
  this.running = false
78
78
  this.excludedNodes.clear()
79
79
  logger.trace(`Network split avoided`)
@@ -1,27 +1,27 @@
1
1
  import { scheduleAtInterval } from '@streamr/utils'
2
- import { EntryPointDiscovery } from './EntryPointDiscovery'
3
- import { Layer1Node } from './Layer1Node'
2
+ import { PeerDescriptorStoreManager } from './PeerDescriptorStoreManager'
3
+ import { DiscoveryLayerNode } from './DiscoveryLayerNode'
4
4
 
5
5
  const DEFAULT_RECONNECT_INTERVAL = 30 * 1000
6
6
  export class StreamPartReconnect {
7
7
  private abortController?: AbortController
8
- private readonly layer1Node: Layer1Node
9
- private readonly entryPointDiscovery: EntryPointDiscovery
8
+ private readonly discoveryLayerNode: DiscoveryLayerNode
9
+ private readonly peerDescriptorStoreManager: PeerDescriptorStoreManager
10
10
 
11
- constructor(layer1Node: Layer1Node, entryPointDiscovery: EntryPointDiscovery) {
12
- this.layer1Node = layer1Node
13
- this.entryPointDiscovery = entryPointDiscovery
11
+ constructor(discoveryLayerNode: DiscoveryLayerNode, peerDescriptorStoreManager: PeerDescriptorStoreManager) {
12
+ this.discoveryLayerNode = discoveryLayerNode
13
+ this.peerDescriptorStoreManager = peerDescriptorStoreManager
14
14
  }
15
15
 
16
16
  async reconnect(timeout = DEFAULT_RECONNECT_INTERVAL): Promise<void> {
17
17
  this.abortController = new AbortController()
18
18
  await scheduleAtInterval(async () => {
19
- const entryPoints = await this.entryPointDiscovery.discoverEntryPoints()
20
- await this.layer1Node.joinDht(entryPoints)
21
- if (this.entryPointDiscovery.isLocalNodeEntryPoint()) {
22
- await this.entryPointDiscovery.storeAndKeepLocalNodeAsEntryPoint()
19
+ const entryPoints = await this.peerDescriptorStoreManager.fetchNodes()
20
+ await this.discoveryLayerNode.joinDht(entryPoints)
21
+ if (this.peerDescriptorStoreManager.isLocalNodeStored()) {
22
+ await this.peerDescriptorStoreManager.storeAndKeepLocalNode()
23
23
  }
24
- if (this.layer1Node.getNeighborCount() > 0) {
24
+ if (this.discoveryLayerNode.getNeighborCount() > 0) {
25
25
  this.abortController!.abort()
26
26
  }
27
27
  }, timeout, true, this.abortController.signal)