@streamr/trackerless-network 100.0.0-testnet-one.4 → 100.0.0-testnet-two.1

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 (125) hide show
  1. package/dist/package.json +7 -7
  2. package/dist/src/NetworkStack.d.ts +1 -5
  3. package/dist/src/NetworkStack.js +1 -3
  4. package/dist/src/NetworkStack.js.map +1 -1
  5. package/dist/src/logic/DeliveryRpcLocal.d.ts +1 -1
  6. package/dist/src/logic/DeliveryRpcLocal.js +3 -3
  7. package/dist/src/logic/DeliveryRpcLocal.js.map +1 -1
  8. package/dist/src/logic/DeliveryRpcRemote.d.ts +3 -3
  9. package/dist/src/logic/DeliveryRpcRemote.js +3 -2
  10. package/dist/src/logic/DeliveryRpcRemote.js.map +1 -1
  11. package/dist/src/logic/EntryPointDiscovery.d.ts +3 -0
  12. package/dist/src/logic/EntryPointDiscovery.js +11 -5
  13. package/dist/src/logic/EntryPointDiscovery.js.map +1 -1
  14. package/dist/src/logic/RandomGraphNode.d.ts +10 -8
  15. package/dist/src/logic/RandomGraphNode.js +23 -17
  16. package/dist/src/logic/RandomGraphNode.js.map +1 -1
  17. package/dist/src/logic/StreamrNode.js +13 -4
  18. package/dist/src/logic/StreamrNode.js.map +1 -1
  19. package/dist/src/logic/formStreamPartDeliveryServiceId.d.ts +2 -1
  20. package/dist/src/logic/formStreamPartDeliveryServiceId.js.map +1 -1
  21. package/dist/src/logic/inspect/Inspector.d.ts +5 -11
  22. package/dist/src/logic/inspect/Inspector.js +2 -3
  23. package/dist/src/logic/inspect/Inspector.js.map +1 -1
  24. package/dist/src/logic/neighbor-discovery/HandshakeRpcLocal.d.ts +3 -3
  25. package/dist/src/logic/neighbor-discovery/HandshakeRpcLocal.js +39 -13
  26. package/dist/src/logic/neighbor-discovery/HandshakeRpcLocal.js.map +1 -1
  27. package/dist/src/logic/neighbor-discovery/HandshakeRpcRemote.d.ts +5 -3
  28. package/dist/src/logic/neighbor-discovery/HandshakeRpcRemote.js +18 -8
  29. package/dist/src/logic/neighbor-discovery/HandshakeRpcRemote.js.map +1 -1
  30. package/dist/src/logic/neighbor-discovery/Handshaker.d.ts +1 -6
  31. package/dist/src/logic/neighbor-discovery/Handshaker.js +6 -6
  32. package/dist/src/logic/neighbor-discovery/Handshaker.js.map +1 -1
  33. package/dist/src/logic/neighbor-discovery/NeighborFinder.d.ts +1 -6
  34. package/dist/src/logic/neighbor-discovery/NeighborFinder.js.map +1 -1
  35. package/dist/src/logic/neighbor-discovery/NeighborUpdateManager.d.ts +3 -8
  36. package/dist/src/logic/neighbor-discovery/NeighborUpdateManager.js +1 -3
  37. package/dist/src/logic/neighbor-discovery/NeighborUpdateManager.js.map +1 -1
  38. package/dist/src/logic/neighbor-discovery/NeighborUpdateRpcLocal.d.ts +2 -2
  39. package/dist/src/logic/neighbor-discovery/NeighborUpdateRpcLocal.js +1 -2
  40. package/dist/src/logic/neighbor-discovery/NeighborUpdateRpcLocal.js.map +1 -1
  41. package/dist/src/logic/neighbor-discovery/NeighborUpdateRpcRemote.d.ts +2 -2
  42. package/dist/src/logic/neighbor-discovery/NeighborUpdateRpcRemote.js.map +1 -1
  43. package/dist/src/logic/proxy/ProxyClient.d.ts +4 -1
  44. package/dist/src/logic/proxy/ProxyClient.js +7 -6
  45. package/dist/src/logic/proxy/ProxyClient.js.map +1 -1
  46. package/dist/src/logic/proxy/ProxyConnectionRpcLocal.js +2 -3
  47. package/dist/src/logic/proxy/ProxyConnectionRpcLocal.js.map +1 -1
  48. package/dist/src/logic/proxy/ProxyConnectionRpcRemote.d.ts +2 -2
  49. package/dist/src/logic/proxy/ProxyConnectionRpcRemote.js.map +1 -1
  50. package/dist/src/logic/temporary-connection/TemporaryConnectionRpcLocal.js +2 -2
  51. package/dist/src/logic/temporary-connection/TemporaryConnectionRpcLocal.js.map +1 -1
  52. package/dist/src/logic/temporary-connection/TemporaryConnectionRpcRemote.d.ts +2 -2
  53. package/dist/src/logic/temporary-connection/TemporaryConnectionRpcRemote.js.map +1 -1
  54. package/dist/src/proto/packages/dht/protos/DhtRpc.client.d.ts +21 -22
  55. package/dist/src/proto/packages/dht/protos/DhtRpc.client.js +18 -18
  56. package/dist/src/proto/packages/dht/protos/DhtRpc.client.js.map +1 -1
  57. package/dist/src/proto/packages/dht/protos/DhtRpc.d.ts +49 -65
  58. package/dist/src/proto/packages/dht/protos/DhtRpc.js +43 -58
  59. package/dist/src/proto/packages/dht/protos/DhtRpc.js.map +1 -1
  60. package/dist/src/proto/packages/dht/protos/DhtRpc.server.d.ts +11 -12
  61. package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.client.d.ts +6 -5
  62. package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.client.js +2 -2
  63. package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.client.js.map +1 -1
  64. package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.d.ts +26 -10
  65. package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.js +21 -9
  66. package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.js.map +1 -1
  67. package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.server.d.ts +4 -3
  68. package/dist/test/benchmark/first-message.js +4 -6
  69. package/dist/test/benchmark/first-message.js.map +1 -1
  70. package/dist/test/utils/utils.d.ts +1 -1
  71. package/dist/test/utils/utils.js +8 -7
  72. package/dist/test/utils/utils.js.map +1 -1
  73. package/package.json +7 -7
  74. package/protos/NetworkRpc.proto +8 -4
  75. package/src/NetworkStack.ts +1 -7
  76. package/src/logic/DeliveryRpcLocal.ts +4 -4
  77. package/src/logic/DeliveryRpcRemote.ts +5 -4
  78. package/src/logic/EntryPointDiscovery.ts +10 -6
  79. package/src/logic/RandomGraphNode.ts +38 -25
  80. package/src/logic/StreamrNode.ts +22 -5
  81. package/src/logic/formStreamPartDeliveryServiceId.ts +2 -1
  82. package/src/logic/inspect/Inspector.ts +15 -16
  83. package/src/logic/neighbor-discovery/HandshakeRpcLocal.ts +46 -15
  84. package/src/logic/neighbor-discovery/HandshakeRpcRemote.ts +21 -10
  85. package/src/logic/neighbor-discovery/Handshaker.ts +15 -24
  86. package/src/logic/neighbor-discovery/NeighborFinder.ts +1 -7
  87. package/src/logic/neighbor-discovery/NeighborUpdateManager.ts +10 -12
  88. package/src/logic/neighbor-discovery/NeighborUpdateRpcLocal.ts +4 -4
  89. package/src/logic/neighbor-discovery/NeighborUpdateRpcRemote.ts +2 -2
  90. package/src/logic/proxy/ProxyClient.ts +19 -7
  91. package/src/logic/proxy/ProxyConnectionRpcLocal.ts +3 -3
  92. package/src/logic/proxy/ProxyConnectionRpcRemote.ts +3 -3
  93. package/src/logic/temporary-connection/TemporaryConnectionRpcLocal.ts +3 -2
  94. package/src/logic/temporary-connection/TemporaryConnectionRpcRemote.ts +2 -2
  95. package/src/proto/packages/dht/protos/DhtRpc.client.ts +31 -32
  96. package/src/proto/packages/dht/protos/DhtRpc.server.ts +11 -12
  97. package/src/proto/packages/dht/protos/DhtRpc.ts +67 -90
  98. package/src/proto/packages/trackerless-network/protos/NetworkRpc.client.ts +7 -6
  99. package/src/proto/packages/trackerless-network/protos/NetworkRpc.server.ts +4 -3
  100. package/src/proto/packages/trackerless-network/protos/NetworkRpc.ts +36 -15
  101. package/test/benchmark/first-message.ts +8 -6
  102. package/test/end-to-end/random-graph-with-real-connections.test.ts +10 -5
  103. package/test/end-to-end/webrtc-full-node-network.test.ts +1 -1
  104. package/test/end-to-end/websocket-full-node-network.test.ts +2 -2
  105. package/test/integration/DeliveryRpcRemote.test.ts +3 -3
  106. package/test/integration/HandshakeRpcRemote.test.ts +2 -4
  107. package/test/integration/Handshakes.test.ts +8 -7
  108. package/test/integration/Inspect.test.ts +0 -2
  109. package/test/integration/NeighborUpdateRpcRemote.test.ts +2 -2
  110. package/test/integration/NetworkNode.test.ts +0 -2
  111. package/test/integration/NetworkRpc.test.ts +0 -3
  112. package/test/integration/RandomGraphNode-Layer1Node-Latencies.test.ts +4 -4
  113. package/test/integration/RandomGraphNode-Layer1Node.test.ts +4 -5
  114. package/test/integration/stream-without-default-entrypoints.test.ts +4 -7
  115. package/test/integration/streamEntryPointReplacing.test.ts +94 -0
  116. package/test/unit/DeliveryRpcLocal.test.ts +2 -1
  117. package/test/unit/EntrypointDiscovery.test.ts +5 -2
  118. package/test/unit/HandshakeRpcLocal.test.ts +47 -9
  119. package/test/unit/NodeList.test.ts +10 -12
  120. package/test/unit/ProxyConnectionRpcRemote.test.ts +18 -10
  121. package/test/unit/RandomGraphNode.test.ts +6 -4
  122. package/test/utils/mock/MockHandshaker.ts +3 -2
  123. package/test/utils/mock/MockNeighborFinder.ts +3 -2
  124. package/test/utils/mock/MockNeighborUpdateManager.ts +3 -2
  125. package/test/utils/utils.ts +16 -8
@@ -51,7 +51,7 @@ const exponentialRunOff = async (
51
51
 
52
52
  const logger = new Logger(module)
53
53
 
54
- const ENTRYPOINT_STORE_LIMIT = 8
54
+ export const ENTRYPOINT_STORE_LIMIT = 8
55
55
  export const NETWORK_SPLIT_AVOIDANCE_LIMIT = 4
56
56
 
57
57
  interface EntryPointDiscoveryConfig {
@@ -69,16 +69,14 @@ export class EntryPointDiscovery {
69
69
  private readonly config: EntryPointDiscoveryConfig
70
70
  private readonly storeInterval: number
71
71
  private readonly networkSplitAvoidedNodes: Set<NodeID> = new Set()
72
-
72
+ private isLocalNodeStoredAsEntryPoint = false
73
73
  constructor(config: EntryPointDiscoveryConfig) {
74
74
  this.config = config
75
75
  this.abortController = new AbortController()
76
76
  this.storeInterval = this.config.storeInterval ?? 60000
77
77
  }
78
78
 
79
- async discoverEntryPointsFromDht(
80
- knownEntryPointCount: number
81
- ): Promise<FindEntryPointsResult> {
79
+ async discoverEntryPointsFromDht(knownEntryPointCount: number): Promise<FindEntryPointsResult> {
82
80
  if (knownEntryPointCount > 0) {
83
81
  return {
84
82
  entryPointsFromDht: false,
@@ -124,6 +122,7 @@ export class EntryPointDiscovery {
124
122
  }
125
123
  const possibleNetworkSplitDetected = this.config.layer1Node.getNumberOfNeighbors() < NETWORK_SPLIT_AVOIDANCE_LIMIT
126
124
  if ((currentEntrypointCount < ENTRYPOINT_STORE_LIMIT) || possibleNetworkSplitDetected) {
125
+ this.isLocalNodeStoredAsEntryPoint = true
127
126
  await this.storeSelfAsEntryPoint()
128
127
  await this.keepSelfAsEntryPoint()
129
128
  }
@@ -165,7 +164,8 @@ export class EntryPointDiscovery {
165
164
  if (this.config.layer1Node.getNumberOfNeighbors() < NETWORK_SPLIT_AVOIDANCE_LIMIT) {
166
165
  // Filter out nodes that are not neighbors as those nodes are assumed to be offline
167
166
  const nodesToAvoid = rediscoveredEntrypoints
168
- .filter((peer) => !this.config.layer1Node.getAllNeighborPeerDescriptors().includes(peer))
167
+ .filter((peer) => !this.config.layer1Node.getAllNeighborPeerDescriptors()
168
+ .some((neighbor) => areEqualPeerDescriptors(neighbor, peer)))
169
169
  .map((peer) => getNodeIdFromPeerDescriptor(peer))
170
170
  nodesToAvoid.forEach((node) => this.networkSplitAvoidedNodes.add(node))
171
171
  throw new Error(`Network split is still possible`)
@@ -175,6 +175,10 @@ export class EntryPointDiscovery {
175
175
  logger.trace(`Network split avoided`)
176
176
  }
177
177
 
178
+ public isLocalNodeEntryPoint(): boolean {
179
+ return this.isLocalNodeStoredAsEntryPoint
180
+ }
181
+
178
182
  async destroy(): Promise<void> {
179
183
  this.abortController.abort()
180
184
  await this.config.deleteEntryPointData(streamPartIdToDataKey(this.config.streamPartId))
@@ -19,23 +19,24 @@ import { DeliveryRpcRemote } from './DeliveryRpcRemote'
19
19
  import { IDeliveryRpc } from '../proto/packages/trackerless-network/protos/NetworkRpc.server'
20
20
  import { DuplicateMessageDetector } from './DuplicateMessageDetector'
21
21
  import { Logger, addManagedEventListener } from '@streamr/utils'
22
- import { toProtoRpcClient } from '@streamr/proto-rpc'
23
- import { IHandshaker } from './neighbor-discovery/Handshaker'
22
+ import { Handshaker } from './neighbor-discovery/Handshaker'
24
23
  import { Propagation } from './propagation/Propagation'
25
- import { INeighborFinder } from './neighbor-discovery/NeighborFinder'
26
- import { INeighborUpdateManager } from './neighbor-discovery/NeighborUpdateManager'
24
+ import { NeighborFinder } from './neighbor-discovery/NeighborFinder'
25
+ import { NeighborUpdateManager } from './neighbor-discovery/NeighborUpdateManager'
27
26
  import { DeliveryRpcLocal } from './DeliveryRpcLocal'
28
27
  import { ProxyConnectionRpcLocal } from './proxy/ProxyConnectionRpcLocal'
29
- import { IInspector } from './inspect/Inspector'
28
+ import { Inspector } from './inspect/Inspector'
30
29
  import { TemporaryConnectionRpcLocal } from './temporary-connection/TemporaryConnectionRpcLocal'
31
30
  import { markAndCheckDuplicate } from './utils'
32
31
  import { NodeID, getNodeIdFromPeerDescriptor } from '../identifiers'
33
32
  import { Layer1Node } from './Layer1Node'
34
33
  import { StreamPartID } from '@streamr/protocol'
34
+ import { uniqBy } from 'lodash'
35
35
 
36
36
  export interface Events {
37
37
  message: (message: StreamMessage) => void
38
38
  targetNeighborConnected: (nodeId: NodeID) => void
39
+ entryPointLeaveDetected: () => void
39
40
  }
40
41
 
41
42
  export interface StrictRandomGraphNodeConfig {
@@ -48,14 +49,16 @@ export interface StrictRandomGraphNodeConfig {
48
49
  nearbyNodeView: NodeList
49
50
  randomNodeView: NodeList
50
51
  targetNeighbors: NodeList
51
- handshaker: IHandshaker
52
- neighborFinder: INeighborFinder
53
- neighborUpdateManager: INeighborUpdateManager
52
+ handshaker: Handshaker
53
+ neighborFinder: NeighborFinder
54
+ neighborUpdateManager: NeighborUpdateManager
54
55
  propagation: Propagation
55
56
  rpcCommunicator: ListeningRpcCommunicator
56
57
  numOfTargetNeighbors: number
57
- inspector: IInspector
58
+ inspector: Inspector
58
59
  temporaryConnectionRpcLocal: TemporaryConnectionRpcLocal
60
+ isLocalNodeEntryPoint: () => boolean
61
+
59
62
  proxyConnectionRpcLocal?: ProxyConnectionRpcLocal
60
63
  rpcRequestTimeout?: number
61
64
  }
@@ -80,19 +83,25 @@ export class RandomGraphNode extends EventEmitter<Events> {
80
83
  rpcCommunicator: this.config.rpcCommunicator,
81
84
  markAndCheckDuplicate: (msg: MessageID, prev?: MessageRef) => markAndCheckDuplicate(this.duplicateDetectors, msg, prev),
82
85
  broadcast: (message: StreamMessage, previousNode?: NodeID) => this.broadcast(message, previousNode),
83
- onLeaveNotice: (senderId: NodeID) => {
84
- const contact = this.config.nearbyNodeView.get(senderId)
85
- || this.config.randomNodeView.get(senderId)
86
- || this.config.targetNeighbors.get(senderId)
87
- || this.config.proxyConnectionRpcLocal?.getConnection(senderId )?.remote
86
+ onLeaveNotice: (sourceId: NodeID, sourceIsStreamEntryPoint: boolean) => {
87
+ if (this.abortController.signal.aborted) {
88
+ return
89
+ }
90
+ const contact = this.config.nearbyNodeView.get(sourceId)
91
+ || this.config.randomNodeView.get(sourceId)
92
+ || this.config.targetNeighbors.get(sourceId)
93
+ || this.config.proxyConnectionRpcLocal?.getConnection(sourceId )?.remote
88
94
  // TODO: check integrity of notifier?
89
95
  if (contact) {
90
96
  this.config.layer1Node.removeContact(contact.getPeerDescriptor())
91
97
  this.config.targetNeighbors.remove(contact.getPeerDescriptor())
92
98
  this.config.nearbyNodeView.remove(contact.getPeerDescriptor())
93
99
  this.config.connectionLocker.unlockConnection(contact.getPeerDescriptor(), this.config.streamPartId)
94
- this.config.neighborFinder.start([senderId])
95
- this.config.proxyConnectionRpcLocal?.removeConnection(senderId)
100
+ this.config.neighborFinder.start([sourceId])
101
+ this.config.proxyConnectionRpcLocal?.removeConnection(sourceId)
102
+ }
103
+ if (sourceIsStreamEntryPoint) {
104
+ this.emit('entryPointLeaveDetected')
96
105
  }
97
106
  },
98
107
  markForInspection: (senderId: NodeID, messageId: MessageID) => this.config.inspector.markMessage(senderId, messageId)
@@ -191,7 +200,8 @@ export class RandomGraphNode extends EventEmitter<Events> {
191
200
  this.config.localPeerDescriptor,
192
201
  descriptor,
193
202
  this.config.streamPartId,
194
- toProtoRpcClient(new DeliveryRpcClient(this.config.rpcCommunicator.getRpcClientTransport())),
203
+ this.config.rpcCommunicator,
204
+ DeliveryRpcClient,
195
205
  this.config.rpcRequestTimeout
196
206
  )
197
207
  ))
@@ -204,7 +214,8 @@ export class RandomGraphNode extends EventEmitter<Events> {
204
214
  this.config.localPeerDescriptor,
205
215
  descriptor,
206
216
  this.config.streamPartId,
207
- toProtoRpcClient(new DeliveryRpcClient(this.config.rpcCommunicator.getRpcClientTransport())),
217
+ this.config.rpcCommunicator,
218
+ DeliveryRpcClient,
208
219
  this.config.rpcRequestTimeout
209
220
 
210
221
  )
@@ -221,7 +232,8 @@ export class RandomGraphNode extends EventEmitter<Events> {
221
232
  this.config.localPeerDescriptor,
222
233
  descriptor,
223
234
  this.config.streamPartId,
224
- toProtoRpcClient(new DeliveryRpcClient(this.config.rpcCommunicator.getRpcClientTransport())),
235
+ this.config.rpcCommunicator,
236
+ DeliveryRpcClient,
225
237
  this.config.rpcRequestTimeout
226
238
  )
227
239
  ))
@@ -240,7 +252,8 @@ export class RandomGraphNode extends EventEmitter<Events> {
240
252
  this.config.localPeerDescriptor,
241
253
  descriptor,
242
254
  this.config.streamPartId,
243
- toProtoRpcClient(new DeliveryRpcClient(this.config.rpcCommunicator.getRpcClientTransport())),
255
+ this.config.rpcCommunicator,
256
+ DeliveryRpcClient,
244
257
  this.config.rpcRequestTimeout
245
258
  )
246
259
  ))
@@ -256,14 +269,14 @@ export class RandomGraphNode extends EventEmitter<Events> {
256
269
  }
257
270
 
258
271
  private getNeighborCandidatesFromLayer1(): PeerDescriptor[] {
259
- const uniqueNodes = new Set<PeerDescriptor>()
272
+ const nodes: PeerDescriptor[] = []
260
273
  this.config.layer1Node.getClosestContacts(this.config.nodeViewSize).forEach((peer: PeerDescriptor) => {
261
- uniqueNodes.add(peer)
274
+ nodes.push(peer)
262
275
  })
263
276
  this.config.layer1Node.getAllNeighborPeerDescriptors().forEach((peer: PeerDescriptor) => {
264
- uniqueNodes.add(peer)
277
+ nodes.push(peer)
265
278
  })
266
- return Array.from(uniqueNodes)
279
+ return uniqBy(nodes, (p) => getNodeIdFromPeerDescriptor(p))
267
280
  }
268
281
 
269
282
  hasProxyConnection(nodeId: NodeID): boolean {
@@ -279,7 +292,7 @@ export class RandomGraphNode extends EventEmitter<Events> {
279
292
  }
280
293
  this.abortController.abort()
281
294
  this.config.proxyConnectionRpcLocal?.stop()
282
- this.config.targetNeighbors.getAll().map((remote) => remote.leaveStreamPartNotice())
295
+ this.config.targetNeighbors.getAll().map((remote) => remote.leaveStreamPartNotice(this.config.isLocalNodeEntryPoint()))
283
296
  this.config.rpcCommunicator.destroy()
284
297
  this.removeAllListeners()
285
298
  this.config.nearbyNodeView.stop()
@@ -130,7 +130,6 @@ export class StreamrNode extends EventEmitter<Events> {
130
130
  return
131
131
  }
132
132
  const layer1Node = this.createLayer1Node(streamPartId, this.knownStreamPartEntryPoints.get(streamPartId) ?? [])
133
- const node = this.createRandomGraphNode(streamPartId, layer1Node)
134
133
  const entryPointDiscovery = new EntryPointDiscovery({
135
134
  streamPartId,
136
135
  localPeerDescriptor: this.getPeerDescriptor(),
@@ -139,6 +138,11 @@ export class StreamrNode extends EventEmitter<Events> {
139
138
  storeEntryPointData: (key, data) => this.layer0Node!.storeDataToDht(key, data),
140
139
  deleteEntryPointData: async (key: Uint8Array) => this.layer0Node!.deleteDataFromDht(key, false)
141
140
  })
141
+ const node = this.createRandomGraphNode(
142
+ streamPartId,
143
+ layer1Node,
144
+ () => entryPointDiscovery.isLocalNodeEntryPoint()
145
+ )
142
146
  streamPart = {
143
147
  proxied: false,
144
148
  layer1Node,
@@ -155,6 +159,14 @@ export class StreamrNode extends EventEmitter<Events> {
155
159
  node.on('message', (message: StreamMessage) => {
156
160
  this.emit('newMessage', message)
157
161
  })
162
+ const handleEntryPointLeave = async () => {
163
+ if (this.destroyed || entryPointDiscovery.isLocalNodeEntryPoint() || this.knownStreamPartEntryPoints.has(streamPartId)) {
164
+ return
165
+ }
166
+ const entryPoints = await entryPointDiscovery.discoverEntryPointsFromDht(0)
167
+ await entryPointDiscovery.storeSelfAsEntryPointIfNecessary(entryPoints.discoveredEntryPoints.length)
168
+ }
169
+ node.on('entryPointLeaveDetected', () => handleEntryPointLeave())
158
170
  setImmediate(async () => {
159
171
  try {
160
172
  await this.startLayersAndJoinDht(streamPartId, entryPointDiscovery)
@@ -190,13 +202,17 @@ export class StreamrNode extends EventEmitter<Events> {
190
202
  serviceId: 'layer1::' + streamPartId,
191
203
  peerDescriptor: this.layer0Node!.getLocalPeerDescriptor(),
192
204
  entryPoints,
193
- numberOfNodesPerKBucket: 4,
205
+ numberOfNodesPerKBucket: 4, // TODO use config option or named constant?
194
206
  rpcRequestTimeout: EXISTING_CONNECTION_TIMEOUT,
195
- dhtJoinTimeout: 20000
207
+ dhtJoinTimeout: 20000 // TODO use config option or named constant?
196
208
  })
197
209
  }
198
210
 
199
- private createRandomGraphNode(streamPartId: StreamPartID, layer1Node: Layer1Node) {
211
+ private createRandomGraphNode(
212
+ streamPartId: StreamPartID,
213
+ layer1Node: Layer1Node,
214
+ isLocalNodeEntryPoint: () => boolean
215
+ ) {
200
216
  return createRandomGraphNode({
201
217
  streamPartId,
202
218
  transport: this.transport!,
@@ -206,7 +222,8 @@ export class StreamrNode extends EventEmitter<Events> {
206
222
  minPropagationTargets: this.config.streamPartitionMinPropagationTargets,
207
223
  numOfTargetNeighbors: this.config.streamPartitionNumOfNeighbors,
208
224
  acceptProxyConnections: this.config.acceptProxyConnections,
209
- rpcRequestTimeout: this.config.rpcRequestTimeout
225
+ rpcRequestTimeout: this.config.rpcRequestTimeout,
226
+ isLocalNodeEntryPoint
210
227
  })
211
228
  }
212
229
 
@@ -1,5 +1,6 @@
1
+ import { ServiceID } from '@streamr/dht'
1
2
  import { StreamPartID } from '@streamr/protocol'
2
3
 
3
- export const formStreamPartDeliveryServiceId = (streamPartId: StreamPartID): string => {
4
+ export const formStreamPartDeliveryServiceId = (streamPartId: StreamPartID): ServiceID => {
4
5
  return `stream-part-delivery-${streamPartId}`
5
6
  }
@@ -1,8 +1,8 @@
1
- import { PeerDescriptor, ConnectionLocker } from '@streamr/dht'
1
+ import { PeerDescriptor, ConnectionLocker, LockID } from '@streamr/dht'
2
2
  import { MessageID } from '../../proto/packages/trackerless-network/protos/NetworkRpc'
3
3
  import { InspectSession, Events as InspectSessionEvents } from './InspectSession'
4
4
  import { TemporaryConnectionRpcClient } from '../../proto/packages/trackerless-network/protos/NetworkRpc.client'
5
- import { ProtoRpcClient, RpcCommunicator, toProtoRpcClient } from '@streamr/proto-rpc'
5
+ import { RpcCommunicator } from '@streamr/proto-rpc'
6
6
  import { Logger, waitForEvent3 } from '@streamr/utils'
7
7
  import { TemporaryConnectionRpcRemote } from '../temporary-connection/TemporaryConnectionRpcRemote'
8
8
  import { NodeID, getNodeIdFromPeerDescriptor } from '../../identifiers'
@@ -14,40 +14,39 @@ interface InspectorConfig {
14
14
  rpcCommunicator: RpcCommunicator
15
15
  connectionLocker: ConnectionLocker
16
16
  inspectionTimeout?: number
17
- openInspectConnection?: (peerDescriptor: PeerDescriptor, lockId: string) => Promise<void>
18
- }
19
-
20
- export interface IInspector {
21
- inspect(peerDescriptor: PeerDescriptor): Promise<boolean>
22
- markMessage(sender: NodeID, messageId: MessageID): void
23
- isInspected(nodeId: NodeID): boolean
24
- stop(): void
17
+ openInspectConnection?: (peerDescriptor: PeerDescriptor, lockId: LockID) => Promise<void>
25
18
  }
26
19
 
27
20
  const logger = new Logger(module)
28
21
  const DEFAULT_TIMEOUT = 60 * 1000
29
22
 
30
- export class Inspector implements IInspector {
23
+ export class Inspector {
31
24
 
32
25
  private readonly sessions: Map<NodeID, InspectSession> = new Map()
33
26
  private readonly streamPartId: StreamPartID
34
- private readonly client: ProtoRpcClient<TemporaryConnectionRpcClient>
35
27
  private readonly localPeerDescriptor: PeerDescriptor
28
+ private readonly rpcCommunicator: RpcCommunicator
36
29
  private readonly connectionLocker: ConnectionLocker
37
30
  private readonly inspectionTimeout: number
38
- private readonly openInspectConnection: (peerDescriptor: PeerDescriptor, lockId: string) => Promise<void>
31
+ private readonly openInspectConnection: (peerDescriptor: PeerDescriptor, lockId: LockID) => Promise<void>
39
32
 
40
33
  constructor(config: InspectorConfig) {
41
34
  this.streamPartId = config.streamPartId
42
35
  this.localPeerDescriptor = config.localPeerDescriptor
43
- this.client = toProtoRpcClient(new TemporaryConnectionRpcClient(config.rpcCommunicator.getRpcClientTransport()))
36
+ this.rpcCommunicator = config.rpcCommunicator
44
37
  this.connectionLocker = config.connectionLocker
45
38
  this.inspectionTimeout = config.inspectionTimeout ?? DEFAULT_TIMEOUT
46
39
  this.openInspectConnection = config.openInspectConnection ?? this.defaultOpenInspectConnection
47
40
  }
48
41
 
49
- async defaultOpenInspectConnection(peerDescriptor: PeerDescriptor, lockId: string): Promise<void> {
50
- const rpcRemote = new TemporaryConnectionRpcRemote(this.localPeerDescriptor, peerDescriptor, this.streamPartId, this.client)
42
+ async defaultOpenInspectConnection(peerDescriptor: PeerDescriptor, lockId: LockID): Promise<void> {
43
+ const rpcRemote = new TemporaryConnectionRpcRemote(
44
+ this.localPeerDescriptor,
45
+ peerDescriptor,
46
+ this.streamPartId,
47
+ this.rpcCommunicator,
48
+ TemporaryConnectionRpcClient
49
+ )
51
50
  await rpcRemote.openConnection()
52
51
  this.connectionLocker.lockConnection(peerDescriptor, lockId)
53
52
  }
@@ -1,5 +1,9 @@
1
- import { Empty } from '../../proto/google/protobuf/empty'
2
- import { InterleaveNotice, StreamPartHandshakeRequest, StreamPartHandshakeResponse } from '../../proto/packages/trackerless-network/protos/NetworkRpc'
1
+ import {
2
+ InterleaveRequest,
3
+ InterleaveResponse,
4
+ StreamPartHandshakeRequest,
5
+ StreamPartHandshakeResponse
6
+ } from '../../proto/packages/trackerless-network/protos/NetworkRpc'
3
7
  import { ServerCallContext } from '@protobuf-ts/runtime-rpc'
4
8
  import { NodeList } from '../NodeList'
5
9
  import { ConnectionLocker, DhtCallContext, PeerDescriptor } from '@streamr/dht'
@@ -7,7 +11,7 @@ import { IHandshakeRpc } from '../../proto/packages/trackerless-network/protos/N
7
11
  import { HandshakeRpcRemote } from './HandshakeRpcRemote'
8
12
  import { DeliveryRpcRemote } from '../DeliveryRpcRemote'
9
13
  import { NodeID, getNodeIdFromPeerDescriptor } from '../../identifiers'
10
- import { binaryToHex } from '@streamr/utils'
14
+ import { Logger, binaryToHex } from '@streamr/utils'
11
15
  import { StreamPartID } from '@streamr/protocol'
12
16
 
13
17
  interface HandshakeRpcLocalConfig {
@@ -15,12 +19,15 @@ interface HandshakeRpcLocalConfig {
15
19
  targetNeighbors: NodeList
16
20
  connectionLocker: ConnectionLocker
17
21
  ongoingHandshakes: Set<NodeID>
22
+ ongoingInterleaves: Set<NodeID>
18
23
  maxNeighborCount: number
19
24
  createRpcRemote: (target: PeerDescriptor) => HandshakeRpcRemote
20
25
  createDeliveryRpcRemote: (peerDescriptor: PeerDescriptor) => DeliveryRpcRemote
21
26
  handshakeWithInterleaving: (target: PeerDescriptor, senderId: NodeID) => Promise<boolean>
22
27
  }
23
28
 
29
+ const logger = new Logger(module)
30
+
24
31
  export class HandshakeRpcLocal implements IHandshakeRpc {
25
32
 
26
33
  private readonly config: HandshakeRpcLocalConfig
@@ -36,13 +43,17 @@ export class HandshakeRpcLocal implements IHandshakeRpc {
36
43
  private handleRequest(request: StreamPartHandshakeRequest, context: ServerCallContext): StreamPartHandshakeResponse {
37
44
  const senderDescriptor = (context as DhtCallContext).incomingSourceDescriptor!
38
45
  const getInterleaveSourceIds = () => (request.interleaveSourceId !== undefined) ? [binaryToHex(request.interleaveSourceId) as NodeID] : []
39
- if (this.config.targetNeighbors.hasNode(senderDescriptor)
46
+ if (this.config.ongoingInterleaves.has(getNodeIdFromPeerDescriptor(senderDescriptor))) {
47
+ return this.rejectHandshake(request)
48
+ } else if (this.config.targetNeighbors.hasNode(senderDescriptor)
40
49
  || this.config.ongoingHandshakes.has(getNodeIdFromPeerDescriptor(senderDescriptor))
41
50
  ) {
42
51
  return this.acceptHandshake(request, senderDescriptor)
43
52
  } else if (this.config.targetNeighbors.size() + this.config.ongoingHandshakes.size < this.config.maxNeighborCount) {
44
53
  return this.acceptHandshake(request, senderDescriptor)
45
- } else if (this.config.targetNeighbors.size(getInterleaveSourceIds()) >= 2) {
54
+ } else if (this.config.targetNeighbors.size(getInterleaveSourceIds()) - this.config.ongoingInterleaves.size >= 2) {
55
+ // Do not accept the handshakes requests if the target neighbor count can potentially drop below 2
56
+ // due to interleaving. This ensures that a stable number of connections is kept during high churn.
46
57
  return this.acceptHandshakeWithInterleaving(request, senderDescriptor)
47
58
  } else {
48
59
  return this.rejectHandshake(request)
@@ -69,7 +80,9 @@ export class HandshakeRpcLocal implements IHandshakeRpc {
69
80
  }
70
81
 
71
82
  private acceptHandshakeWithInterleaving(request: StreamPartHandshakeRequest, requester: PeerDescriptor): StreamPartHandshakeResponse {
72
- const exclude = request.neighborIds.map((id: Uint8Array) => binaryToHex(id) as NodeID)
83
+ const exclude: NodeID[] = []
84
+ request.neighborIds.forEach((id: Uint8Array) => exclude.push(binaryToHex(id) as NodeID))
85
+ this.config.ongoingInterleaves.forEach((id) => exclude.push(id))
73
86
  exclude.push(getNodeIdFromPeerDescriptor(requester))
74
87
  if (request.interleaveSourceId !== undefined) {
75
88
  exclude.push(binaryToHex(request.interleaveSourceId) as NodeID)
@@ -77,10 +90,25 @@ export class HandshakeRpcLocal implements IHandshakeRpc {
77
90
  const furthest = this.config.targetNeighbors.getFurthest(exclude)
78
91
  const furthestPeerDescriptor = furthest ? furthest.getPeerDescriptor() : undefined
79
92
  if (furthest) {
93
+ const nodeId = getNodeIdFromPeerDescriptor(furthest.getPeerDescriptor())
80
94
  const remote = this.config.createRpcRemote(furthest.getPeerDescriptor())
81
- remote.interleaveNotice(requester)
82
- this.config.targetNeighbors.remove(furthest.getPeerDescriptor())
83
- this.config.connectionLocker.unlockConnection(furthestPeerDescriptor!, this.config.streamPartId)
95
+ this.config.ongoingInterleaves.add(nodeId)
96
+ // Run this with then catch instead of setImmediate to avoid changes in state
97
+ // eslint-disable-next-line promise/catch-or-return
98
+ remote.interleaveRequest(requester).then((response) => {
99
+ // If response is accepted, remove the furthest node from the target neighbors
100
+ // and unlock the connection
101
+ // If response is not accepted, keep the furthest node as a neighbor
102
+ if (response.accepted) {
103
+ this.config.targetNeighbors.remove(furthest.getPeerDescriptor())
104
+ this.config.connectionLocker.unlockConnection(furthestPeerDescriptor!, this.config.streamPartId)
105
+ }
106
+ return
107
+ }).catch(() => {
108
+ // no-op: InterleaveRequest cannot reject
109
+ }).finally(() => {
110
+ this.config.ongoingInterleaves.delete(nodeId)
111
+ })
84
112
  }
85
113
  this.config.targetNeighbors.add(this.config.createDeliveryRpcRemote(requester))
86
114
  this.config.connectionLocker.lockConnection(requester, this.config.streamPartId)
@@ -91,16 +119,19 @@ export class HandshakeRpcLocal implements IHandshakeRpc {
91
119
  }
92
120
  }
93
121
 
94
- async interleaveNotice(message: InterleaveNotice, context: ServerCallContext): Promise<Empty> {
95
- if (message.streamPartId === this.config.streamPartId) {
96
- const senderPeerDescriptor = (context as DhtCallContext).incomingSourceDescriptor!
97
- const senderId = getNodeIdFromPeerDescriptor(senderPeerDescriptor)
122
+ async interleaveRequest(message: InterleaveRequest, context: ServerCallContext): Promise<InterleaveResponse> {
123
+ const senderPeerDescriptor = (context as DhtCallContext).incomingSourceDescriptor!
124
+ const senderId = getNodeIdFromPeerDescriptor(senderPeerDescriptor)
125
+ try {
126
+ await this.config.handshakeWithInterleaving(message.interleaveTargetDescriptor!, senderId)
98
127
  if (this.config.targetNeighbors.hasNodeById(senderId)) {
99
128
  this.config.connectionLocker.unlockConnection(senderPeerDescriptor, this.config.streamPartId)
100
129
  this.config.targetNeighbors.remove(senderPeerDescriptor)
101
130
  }
102
- this.config.handshakeWithInterleaving(message.interleaveTargetDescriptor!, senderId).catch((_e) => {})
131
+ return { accepted: true }
132
+ } catch (err) {
133
+ logger.debug(`interleaveRequest to ${getNodeIdFromPeerDescriptor(message.interleaveTargetDescriptor!)} failed: ${err}`)
134
+ return { accepted: false }
103
135
  }
104
- return Empty
105
136
  }
106
137
  }
@@ -2,8 +2,8 @@ import { PeerDescriptor, RpcRemote } from '@streamr/dht'
2
2
  import { Logger, hexToBinary } from '@streamr/utils'
3
3
  import { v4 } from 'uuid'
4
4
  import { NodeID, getNodeIdFromPeerDescriptor } from '../../identifiers'
5
- import { InterleaveNotice, StreamPartHandshakeRequest } from '../../proto/packages/trackerless-network/protos/NetworkRpc'
6
- import { IHandshakeRpcClient } from '../../proto/packages/trackerless-network/protos/NetworkRpc.client'
5
+ import { InterleaveRequest, InterleaveResponse, StreamPartHandshakeRequest } from '../../proto/packages/trackerless-network/protos/NetworkRpc'
6
+ import { HandshakeRpcClient } from '../../proto/packages/trackerless-network/protos/NetworkRpc.client'
7
7
 
8
8
  const logger = new Logger(module)
9
9
 
@@ -12,7 +12,9 @@ interface HandshakeResponse {
12
12
  interleaveTargetDescriptor?: PeerDescriptor
13
13
  }
14
14
 
15
- export class HandshakeRpcRemote extends RpcRemote<IHandshakeRpcClient> {
15
+ export const INTERLEAVE_REQUEST_TIMEOUT = 15000
16
+
17
+ export class HandshakeRpcRemote extends RpcRemote<HandshakeRpcClient> {
16
18
 
17
19
  async handshake(
18
20
  neighborIds: NodeID[],
@@ -40,16 +42,25 @@ export class HandshakeRpcRemote extends RpcRemote<IHandshakeRpcClient> {
40
42
  }
41
43
  }
42
44
 
43
- interleaveNotice(originatorDescriptor: PeerDescriptor): void {
44
- const notification: InterleaveNotice = {
45
- streamPartId: this.getServiceId(),
45
+ async interleaveRequest(originatorDescriptor: PeerDescriptor): Promise<InterleaveResponse> {
46
+ const request: InterleaveRequest = {
46
47
  interleaveTargetDescriptor: originatorDescriptor
47
48
  }
48
49
  const options = this.formDhtRpcOptions({
49
- notification: true
50
- })
51
- this.getClient().interleaveNotice(notification, options).catch(() => {
52
- logger.debug('Failed to send interleaveNotice')
50
+ connect: false,
51
+ timeout: INTERLEAVE_REQUEST_TIMEOUT
53
52
  })
53
+ try {
54
+ const res = await this.getClient().interleaveRequest(request, options)
55
+ return {
56
+ accepted: res.accepted
57
+ }
58
+ } catch (err) {
59
+ logger.debug(`interleaveRequest to ${getNodeIdFromPeerDescriptor(this.getPeerDescriptor())} failed: ${err}`)
60
+ return {
61
+ accepted: false
62
+ }
63
+ }
64
+
54
65
  }
55
66
  }
@@ -1,19 +1,19 @@
1
1
  import { ConnectionLocker, PeerDescriptor } from '@streamr/dht'
2
2
  import { NodeList } from '../NodeList'
3
3
  import { DeliveryRpcRemote } from '../DeliveryRpcRemote'
4
- import { ProtoRpcClient, RpcCommunicator, toProtoRpcClient } from '@streamr/proto-rpc'
4
+ import { RpcCommunicator } from '@streamr/proto-rpc'
5
5
  import {
6
- HandshakeRpcClient,
7
- IHandshakeRpcClient, DeliveryRpcClient
6
+ DeliveryRpcClient, HandshakeRpcClient
8
7
  } from '../../proto/packages/trackerless-network/protos/NetworkRpc.client'
9
8
  import {
10
- InterleaveNotice,
9
+ InterleaveRequest,
10
+ InterleaveResponse,
11
11
  StreamPartHandshakeRequest,
12
12
  StreamPartHandshakeResponse
13
13
  } from '../../proto/packages/trackerless-network/protos/NetworkRpc'
14
14
  import { Logger } from '@streamr/utils'
15
15
  import { IHandshakeRpc } from '../../proto/packages/trackerless-network/protos/NetworkRpc.server'
16
- import { HandshakeRpcRemote } from './HandshakeRpcRemote'
16
+ import { HandshakeRpcRemote, INTERLEAVE_REQUEST_TIMEOUT } from './HandshakeRpcRemote'
17
17
  import { HandshakeRpcLocal } from './HandshakeRpcLocal'
18
18
  import { NodeID, getNodeIdFromPeerDescriptor } from '../../identifiers'
19
19
  import { StreamPartID } from '@streamr/protocol'
@@ -34,38 +34,33 @@ const logger = new Logger(module)
34
34
 
35
35
  const PARALLEL_HANDSHAKE_COUNT = 2
36
36
 
37
- export interface IHandshaker {
38
- attemptHandshakesOnContacts(excludedIds: NodeID[]): Promise<NodeID[]>
39
- getOngoingHandshakes(): Set<NodeID>
40
- }
41
-
42
- export class Handshaker implements IHandshaker {
37
+ export class Handshaker {
43
38
 
44
39
  private readonly ongoingHandshakes: Set<NodeID> = new Set()
45
40
  private config: HandshakerConfig
46
- private readonly client: ProtoRpcClient<IHandshakeRpcClient>
47
41
  private readonly rpcLocal: IHandshakeRpc
48
42
 
49
43
  constructor(config: HandshakerConfig) {
50
44
  this.config = config
51
- this.client = toProtoRpcClient(new HandshakeRpcClient(this.config.rpcCommunicator.getRpcClientTransport()))
52
45
  this.rpcLocal = new HandshakeRpcLocal({
53
46
  streamPartId: this.config.streamPartId,
54
47
  targetNeighbors: this.config.targetNeighbors,
55
48
  connectionLocker: this.config.connectionLocker,
56
49
  ongoingHandshakes: this.ongoingHandshakes,
50
+ ongoingInterleaves: new Set(),
57
51
  maxNeighborCount: this.config.maxNeighborCount,
58
52
  handshakeWithInterleaving: (target: PeerDescriptor, senderId: NodeID) => this.handshakeWithInterleaving(target, senderId),
59
53
  createRpcRemote: (target: PeerDescriptor) => this.createRpcRemote(target),
60
54
  createDeliveryRpcRemote: (target: PeerDescriptor) => this.createDeliveryRpcRemote(target)
61
55
  })
62
- this.config.rpcCommunicator.registerRpcNotification(InterleaveNotice, 'interleaveNotice',
63
- (req: InterleaveNotice, context) => this.rpcLocal.interleaveNotice(req, context))
56
+ this.config.rpcCommunicator.registerRpcMethod(InterleaveRequest, InterleaveResponse, 'interleaveRequest',
57
+ (req: InterleaveRequest, context) => this.rpcLocal.interleaveRequest(req, context), { timeout: INTERLEAVE_REQUEST_TIMEOUT })
64
58
  this.config.rpcCommunicator.registerRpcMethod(StreamPartHandshakeRequest, StreamPartHandshakeResponse, 'handshake',
65
59
  (req: StreamPartHandshakeRequest, context) => this.rpcLocal.handshake(req, context))
66
60
  }
67
61
 
68
62
  async attemptHandshakesOnContacts(excludedIds: NodeID[]): Promise<NodeID[]> {
63
+ // TODO use config option or named constant? or why the value 2?
69
64
  if (this.config.targetNeighbors.size() + this.ongoingHandshakes.size < this.config.maxNeighborCount - 2) {
70
65
  logger.trace(`Attempting parallel handshakes with ${PARALLEL_HANDSHAKE_COUNT} targets`)
71
66
  return this.selectParallelTargetsAndHandshake(excludedIds)
@@ -142,13 +137,7 @@ export class Handshaker implements IHandshaker {
142
137
  }
143
138
 
144
139
  private async handshakeWithInterleaving(target: PeerDescriptor, interleaveSourceId: NodeID): Promise<boolean> {
145
- const targetNeighbor = new HandshakeRpcRemote(
146
- this.config.localPeerDescriptor,
147
- target,
148
- this.config.streamPartId,
149
- this.client,
150
- this.config.rpcRequestTimeout
151
- )
140
+ const targetNeighbor = this.createRpcRemote(target)
152
141
  const targetNodeId = getNodeIdFromPeerDescriptor(targetNeighbor.getPeerDescriptor())
153
142
  this.ongoingHandshakes.add(targetNodeId)
154
143
  const result = await targetNeighbor.handshake(
@@ -169,7 +158,8 @@ export class Handshaker implements IHandshaker {
169
158
  this.config.localPeerDescriptor,
170
159
  targetPeerDescriptor,
171
160
  this.config.streamPartId,
172
- this.client,
161
+ this.config.rpcCommunicator,
162
+ HandshakeRpcClient,
173
163
  this.config.rpcRequestTimeout
174
164
  )
175
165
  }
@@ -179,7 +169,8 @@ export class Handshaker implements IHandshaker {
179
169
  this.config.localPeerDescriptor,
180
170
  targetPeerDescriptor,
181
171
  this.config.streamPartId,
182
- toProtoRpcClient(new DeliveryRpcClient(this.config.rpcCommunicator.getRpcClientTransport())),
172
+ this.config.rpcCommunicator,
173
+ DeliveryRpcClient,
183
174
  this.config.rpcRequestTimeout
184
175
  )
185
176
  }
@@ -12,13 +12,7 @@ interface FindNeighborsSessionConfig {
12
12
  const INITIAL_WAIT = 100
13
13
  const INTERVAL = 250
14
14
 
15
- export interface INeighborFinder {
16
- start(excluded?: NodeID[]): void
17
- stop(): void
18
- isRunning(): boolean
19
- }
20
-
21
- export class NeighborFinder implements INeighborFinder {
15
+ export class NeighborFinder {
22
16
  private readonly abortController: AbortController
23
17
  private readonly config: FindNeighborsSessionConfig
24
18
  private running = false