@streamr/trackerless-network 0.0.1-tatum.5 → 0.0.1-tatum.6
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.
- package/dist/package.json +6 -6
- package/dist/src/NetworkStack.d.ts +1 -1
- package/dist/src/NetworkStack.js.map +1 -1
- package/dist/src/logic/ILayer1.d.ts +2 -2
- package/dist/src/logic/NodeList.d.ts +2 -2
- package/dist/src/logic/NodeList.js +2 -2
- package/dist/src/logic/NodeList.js.map +1 -1
- package/dist/src/logic/RandomGraphNode.js +6 -6
- package/dist/src/logic/RandomGraphNode.js.map +1 -1
- package/dist/src/logic/StreamPartEntryPointDiscovery.d.ts +7 -9
- package/dist/src/logic/StreamPartEntryPointDiscovery.js +46 -73
- package/dist/src/logic/StreamPartEntryPointDiscovery.js.map +1 -1
- package/dist/src/logic/StreamrNode.d.ts +2 -1
- package/dist/src/logic/StreamrNode.js +22 -15
- package/dist/src/logic/StreamrNode.js.map +1 -1
- package/dist/src/logic/createRandomGraphNode.js +1 -1
- package/dist/src/logic/createRandomGraphNode.js.map +1 -1
- package/dist/src/logic/neighbor-discovery/NeighborFinder.js +4 -4
- package/dist/src/logic/neighbor-discovery/NeighborFinder.js.map +1 -1
- package/dist/src/logic/neighbor-discovery/NeighborUpdateManager.js +2 -2
- package/dist/src/logic/neighbor-discovery/NeighborUpdateManager.js.map +1 -1
- package/dist/src/logic/neighbor-discovery/NeighborUpdateManagerServer.js +2 -2
- package/dist/src/logic/neighbor-discovery/NeighborUpdateManagerServer.js.map +1 -1
- package/dist/src/logic/proxy/ProxyStreamConnectionClient.d.ts +2 -3
- package/dist/src/logic/proxy/ProxyStreamConnectionClient.js +5 -8
- package/dist/src/logic/proxy/ProxyStreamConnectionClient.js.map +1 -1
- package/dist/src/logic/proxy/ProxyStreamConnectionServer.d.ts +0 -2
- package/dist/src/logic/proxy/ProxyStreamConnectionServer.js +0 -6
- package/dist/src/logic/proxy/ProxyStreamConnectionServer.js.map +1 -1
- package/dist/test/utils/utils.js +0 -1
- package/dist/test/utils/utils.js.map +1 -1
- package/package.json +6 -6
- package/src/NetworkStack.ts +1 -1
- package/src/logic/ILayer1.ts +2 -5
- package/src/logic/NodeList.ts +2 -2
- package/src/logic/RandomGraphNode.ts +6 -7
- package/src/logic/StreamPartEntryPointDiscovery.ts +51 -86
- package/src/logic/StreamrNode.ts +23 -21
- package/src/logic/createRandomGraphNode.ts +1 -1
- package/src/logic/neighbor-discovery/NeighborFinder.ts +4 -4
- package/src/logic/neighbor-discovery/NeighborUpdateManager.ts +2 -2
- package/src/logic/neighbor-discovery/NeighborUpdateManagerServer.ts +2 -2
- package/src/logic/proxy/ProxyStreamConnectionClient.ts +6 -10
- package/src/logic/proxy/ProxyStreamConnectionServer.ts +0 -8
- package/test/end-to-end/proxy-connections.test.ts +1 -1
- package/test/end-to-end/websocket-full-node-network.test.ts +1 -1
- package/test/unit/RandomGraphNode.test.ts +6 -6
- package/test/unit/StreamPartEntrypointDiscovery.test.ts +15 -30
- package/test/utils/mock/MockLayer1.ts +5 -7
- package/test/utils/utils.ts +0 -1
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import { createHash } from 'crypto'
|
|
2
1
|
import {
|
|
3
|
-
|
|
2
|
+
DataEntry,
|
|
4
3
|
PeerDescriptor,
|
|
5
4
|
RecursiveFindResult,
|
|
6
|
-
|
|
5
|
+
isSamePeerDescriptor
|
|
7
6
|
} from '@streamr/dht'
|
|
8
|
-
import { Any } from '../proto/google/protobuf/any'
|
|
9
|
-
import { Logger, setAbortableTimeout, wait } from '@streamr/utils'
|
|
10
|
-
import { StreamPartDelivery } from './StreamrNode'
|
|
11
7
|
import { StreamPartID } from '@streamr/protocol'
|
|
8
|
+
import { Logger, scheduleAtInterval, wait } from '@streamr/utils'
|
|
9
|
+
import { createHash } from 'crypto'
|
|
12
10
|
import { NodeID, getNodeIdFromPeerDescriptor } from '../identifiers'
|
|
11
|
+
import { Any } from '../proto/google/protobuf/any'
|
|
13
12
|
import { ILayer1 } from './ILayer1'
|
|
14
13
|
|
|
15
14
|
export const streamPartIdToDataKey = (streamPartId: StreamPartID): Uint8Array => {
|
|
@@ -57,8 +56,9 @@ const ENTRYPOINT_STORE_LIMIT = 8
|
|
|
57
56
|
export const NETWORK_SPLIT_AVOIDANCE_LIMIT = 4
|
|
58
57
|
|
|
59
58
|
interface StreamPartEntryPointDiscoveryConfig {
|
|
60
|
-
|
|
59
|
+
streamPartId: StreamPartID
|
|
61
60
|
ownPeerDescriptor: PeerDescriptor
|
|
61
|
+
layer1: ILayer1
|
|
62
62
|
getEntryPointData: (key: Uint8Array) => Promise<RecursiveFindResult>
|
|
63
63
|
getEntryPointDataViaNode: (key: Uint8Array, node: PeerDescriptor) => Promise<DataEntry[]>
|
|
64
64
|
storeEntryPointData: (key: Uint8Array, data: Any) => Promise<PeerDescriptor[]>
|
|
@@ -69,19 +69,16 @@ interface StreamPartEntryPointDiscoveryConfig {
|
|
|
69
69
|
export class StreamPartEntryPointDiscovery {
|
|
70
70
|
private readonly abortController: AbortController
|
|
71
71
|
private readonly config: StreamPartEntryPointDiscoveryConfig
|
|
72
|
-
private readonly servicedStreamParts: Map<StreamPartID, NodeJS.Timeout>
|
|
73
72
|
private readonly cacheInterval: number
|
|
74
|
-
private readonly networkSplitAvoidedNodes:
|
|
73
|
+
private readonly networkSplitAvoidedNodes: Set<NodeID> = new Set()
|
|
75
74
|
|
|
76
75
|
constructor(config: StreamPartEntryPointDiscoveryConfig) {
|
|
77
76
|
this.config = config
|
|
78
77
|
this.abortController = new AbortController()
|
|
79
78
|
this.cacheInterval = this.config.cacheInterval ?? 60000
|
|
80
|
-
this.servicedStreamParts = new Map()
|
|
81
79
|
}
|
|
82
80
|
|
|
83
81
|
async discoverEntryPointsFromDht(
|
|
84
|
-
streamPartId: StreamPartID,
|
|
85
82
|
knownEntryPointCount: number,
|
|
86
83
|
forwardingNode?: PeerDescriptor
|
|
87
84
|
): Promise<FindEntryPointsResult> {
|
|
@@ -91,7 +88,7 @@ export class StreamPartEntryPointDiscovery {
|
|
|
91
88
|
discoveredEntryPoints: []
|
|
92
89
|
}
|
|
93
90
|
}
|
|
94
|
-
const discoveredEntryPoints = await this.discoverEntryPoints(
|
|
91
|
+
const discoveredEntryPoints = await this.discoverEntryPoints(forwardingNode)
|
|
95
92
|
if (discoveredEntryPoints.length === 0) {
|
|
96
93
|
discoveredEntryPoints.push(this.config.ownPeerDescriptor)
|
|
97
94
|
}
|
|
@@ -101,20 +98,19 @@ export class StreamPartEntryPointDiscovery {
|
|
|
101
98
|
}
|
|
102
99
|
}
|
|
103
100
|
|
|
104
|
-
private async discoverEntryPoints(
|
|
105
|
-
const dataKey = streamPartIdToDataKey(streamPartId)
|
|
106
|
-
|
|
101
|
+
private async discoverEntryPoints(forwardingNode?: PeerDescriptor): Promise<PeerDescriptor[]> {
|
|
102
|
+
const dataKey = streamPartIdToDataKey(this.config.streamPartId)
|
|
103
|
+
const discoveredEntryPoints = forwardingNode ?
|
|
107
104
|
await this.queryEntryPointsViaNode(dataKey, forwardingNode) : await this.queryEntrypoints(dataKey)
|
|
108
105
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
106
|
+
const filtered = discoveredEntryPoints.filter((node) =>
|
|
107
|
+
!this.networkSplitAvoidedNodes.has(getNodeIdFromPeerDescriptor(node)))
|
|
108
|
+
// If all discovered entry points have previously been detected as offline, try again
|
|
109
|
+
if (filtered.length > 0) {
|
|
110
|
+
return filtered
|
|
111
|
+
} else {
|
|
112
|
+
return discoveredEntryPoints
|
|
116
113
|
}
|
|
117
|
-
return discoveredEntryPoints
|
|
118
114
|
}
|
|
119
115
|
|
|
120
116
|
private async queryEntrypoints(key: Uint8Array): Promise<PeerDescriptor[]> {
|
|
@@ -131,6 +127,7 @@ export class StreamPartEntryPointDiscovery {
|
|
|
131
127
|
}
|
|
132
128
|
}
|
|
133
129
|
|
|
130
|
+
// TODO remove this method in NET-1122
|
|
134
131
|
private async queryEntryPointsViaNode(key: Uint8Array, node: PeerDescriptor): Promise<PeerDescriptor[]> {
|
|
135
132
|
logger.trace(`Finding data via node ${this.config.ownPeerDescriptor.nodeName}`)
|
|
136
133
|
try {
|
|
@@ -145,96 +142,64 @@ export class StreamPartEntryPointDiscovery {
|
|
|
145
142
|
}
|
|
146
143
|
}
|
|
147
144
|
|
|
148
|
-
async storeSelfAsEntryPointIfNecessary(
|
|
149
|
-
|
|
150
|
-
entryPointsFromDht: boolean,
|
|
151
|
-
currentEntrypointCount: number
|
|
152
|
-
): Promise<void> {
|
|
153
|
-
if (!this.config.streamParts.has(streamPartId) || !entryPointsFromDht) {
|
|
145
|
+
async storeSelfAsEntryPointIfNecessary(currentEntrypointCount: number): Promise<void> {
|
|
146
|
+
if (this.abortController.signal.aborted) {
|
|
154
147
|
return
|
|
155
148
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
149
|
+
const possibleNetworkSplitDetected = this.config.layer1.getBucketSize() < NETWORK_SPLIT_AVOIDANCE_LIMIT
|
|
150
|
+
if ((currentEntrypointCount < ENTRYPOINT_STORE_LIMIT) || possibleNetworkSplitDetected) {
|
|
151
|
+
await this.storeSelfAsEntryPoint()
|
|
152
|
+
await this.keepSelfAsEntryPoint()
|
|
153
|
+
}
|
|
154
|
+
if (possibleNetworkSplitDetected) {
|
|
155
|
+
setImmediate(() => this.avoidNetworkSplit())
|
|
161
156
|
}
|
|
162
157
|
}
|
|
163
158
|
|
|
164
|
-
private async storeSelfAsEntryPoint(
|
|
159
|
+
private async storeSelfAsEntryPoint(): Promise<void> {
|
|
165
160
|
const ownPeerDescriptor = this.config.ownPeerDescriptor
|
|
166
161
|
const dataToStore = Any.pack(ownPeerDescriptor, PeerDescriptor)
|
|
167
162
|
try {
|
|
168
|
-
await this.config.storeEntryPointData(streamPartIdToDataKey(streamPartId), dataToStore)
|
|
169
|
-
this.keepSelfAsEntryPoint(streamPartId)
|
|
163
|
+
await this.config.storeEntryPointData(streamPartIdToDataKey(this.config.streamPartId), dataToStore)
|
|
170
164
|
} catch (err) {
|
|
171
|
-
logger.warn(`Failed to store self as entrypoint for ${streamPartId}`)
|
|
165
|
+
logger.warn(`Failed to store self as entrypoint for ${this.config.streamPartId}`)
|
|
172
166
|
}
|
|
173
167
|
}
|
|
174
168
|
|
|
175
|
-
private keepSelfAsEntryPoint(
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
this.servicedStreamParts.set(streamPartId, setTimeout(async () => {
|
|
180
|
-
if (!this.config.streamParts.has(streamPartId)) {
|
|
181
|
-
this.servicedStreamParts.delete(streamPartId)
|
|
182
|
-
return
|
|
183
|
-
}
|
|
184
|
-
logger.trace(`Attempting to keep self as entrypoint for ${streamPartId}`)
|
|
169
|
+
private async keepSelfAsEntryPoint(): Promise<void> {
|
|
170
|
+
await scheduleAtInterval(async () => {
|
|
171
|
+
logger.trace(`Attempting to keep self as entrypoint for ${this.config.streamPartId}`)
|
|
185
172
|
try {
|
|
186
|
-
const discovered = await this.discoverEntryPoints(
|
|
173
|
+
const discovered = await this.discoverEntryPoints()
|
|
187
174
|
if (discovered.length < ENTRYPOINT_STORE_LIMIT
|
|
188
175
|
|| discovered.some((peerDescriptor) => isSamePeerDescriptor(peerDescriptor, this.config.ownPeerDescriptor))) {
|
|
189
|
-
await this.storeSelfAsEntryPoint(
|
|
190
|
-
this.servicedStreamParts.delete(streamPartId)
|
|
191
|
-
this.keepSelfAsEntryPoint(streamPartId)
|
|
192
|
-
} else {
|
|
193
|
-
this.servicedStreamParts.delete(streamPartId)
|
|
176
|
+
await this.storeSelfAsEntryPoint()
|
|
194
177
|
}
|
|
195
178
|
} catch (err) {
|
|
196
|
-
logger.debug(`Failed to keep self as entrypoint for ${streamPartId}`)
|
|
179
|
+
logger.debug(`Failed to keep self as entrypoint for ${this.config.streamPartId}`)
|
|
197
180
|
}
|
|
198
|
-
}, this.cacheInterval)
|
|
181
|
+
}, this.cacheInterval, false, this.abortController.signal)
|
|
199
182
|
}
|
|
200
183
|
|
|
201
|
-
private async avoidNetworkSplit(
|
|
184
|
+
private async avoidNetworkSplit(): Promise<void> {
|
|
202
185
|
await exponentialRunOff(async () => {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
186
|
+
const rediscoveredEntrypoints = await this.discoverEntryPoints()
|
|
187
|
+
await this.config.layer1.joinDht(rediscoveredEntrypoints, false, false)
|
|
188
|
+
if (this.config.layer1!.getBucketSize() < NETWORK_SPLIT_AVOIDANCE_LIMIT) {
|
|
189
|
+
// Filter out nodes that are not in the k-bucket, assumed to be offline
|
|
190
|
+
const nodesToAvoid = rediscoveredEntrypoints
|
|
191
|
+
.filter((peer) => !this.config.layer1!.getKBucketPeers().includes(peer))
|
|
192
|
+
.map((peer) => getNodeIdFromPeerDescriptor(peer))
|
|
193
|
+
nodesToAvoid.forEach((node) => this.networkSplitAvoidedNodes.add(node))
|
|
194
|
+
throw new Error(`Network split is still possible`)
|
|
213
195
|
}
|
|
214
196
|
}, 'avoid network split', this.abortController.signal)
|
|
215
|
-
this.networkSplitAvoidedNodes.
|
|
197
|
+
this.networkSplitAvoidedNodes.clear()
|
|
216
198
|
logger.trace(`Network split avoided`)
|
|
217
199
|
}
|
|
218
200
|
|
|
219
|
-
private addAvoidedNodes(streamPartId: StreamPartID, nodesToAvoid: PeerDescriptor[]): void {
|
|
220
|
-
if (!this.networkSplitAvoidedNodes.has(streamPartId)) {
|
|
221
|
-
this.networkSplitAvoidedNodes.set(streamPartId, new Set())
|
|
222
|
-
}
|
|
223
|
-
nodesToAvoid.forEach((node) => this.networkSplitAvoidedNodes.get(streamPartId)!.add(getNodeIdFromPeerDescriptor(node)))
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
removeSelfAsEntryPoint(streamPartId: StreamPartID): void {
|
|
227
|
-
if (this.servicedStreamParts.has(streamPartId)) {
|
|
228
|
-
setAbortableTimeout(() => this.config.deleteEntryPointData(streamPartIdToDataKey(streamPartId)), 0, this.abortController.signal)
|
|
229
|
-
clearTimeout(this.servicedStreamParts.get(streamPartId))
|
|
230
|
-
this.servicedStreamParts.delete(streamPartId)
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
201
|
async destroy(): Promise<void> {
|
|
235
|
-
this.servicedStreamParts.forEach((_, streamPartId) => this.removeSelfAsEntryPoint(streamPartId))
|
|
236
|
-
this.servicedStreamParts.clear()
|
|
237
202
|
this.abortController.abort()
|
|
203
|
+
await this.config.deleteEntryPointData(streamPartIdToDataKey(this.config.streamPartId))
|
|
238
204
|
}
|
|
239
|
-
|
|
240
205
|
}
|
package/src/logic/StreamrNode.ts
CHANGED
|
@@ -31,6 +31,7 @@ export type StreamPartDelivery = {
|
|
|
31
31
|
proxied: false
|
|
32
32
|
layer1: ILayer1
|
|
33
33
|
node: RandomGraphNode
|
|
34
|
+
entryPointDiscovery: StreamPartEntryPointDiscovery
|
|
34
35
|
} | {
|
|
35
36
|
proxied: true
|
|
36
37
|
client: ProxyStreamConnectionClient
|
|
@@ -63,7 +64,6 @@ export class StreamrNode extends EventEmitter<Events> {
|
|
|
63
64
|
private P2PTransport?: ITransport
|
|
64
65
|
private connectionLocker?: ConnectionLocker
|
|
65
66
|
private layer0?: ILayer0
|
|
66
|
-
private streamPartEntryPointDiscovery?: StreamPartEntryPointDiscovery
|
|
67
67
|
private readonly metricsContext: MetricsContext
|
|
68
68
|
private readonly metrics: Metrics
|
|
69
69
|
public config: StreamrNodeConfig
|
|
@@ -93,14 +93,6 @@ export class StreamrNode extends EventEmitter<Events> {
|
|
|
93
93
|
this.layer0 = startedAndJoinedLayer0
|
|
94
94
|
this.P2PTransport = transport
|
|
95
95
|
this.connectionLocker = connectionLocker
|
|
96
|
-
this.streamPartEntryPointDiscovery = new StreamPartEntryPointDiscovery({
|
|
97
|
-
ownPeerDescriptor: this.getPeerDescriptor(),
|
|
98
|
-
streamParts: this.streamParts,
|
|
99
|
-
getEntryPointData: (key) => this.layer0!.getDataFromDht(key),
|
|
100
|
-
getEntryPointDataViaNode: (key, node) => this.layer0!.findDataViaPeer(key, node),
|
|
101
|
-
storeEntryPointData: (key, data) => this.layer0!.storeDataToDht(key, data),
|
|
102
|
-
deleteEntryPointData: (key) => this.layer0!.deleteDataFromDht(key)
|
|
103
|
-
})
|
|
104
96
|
cleanUp = () => this.destroy()
|
|
105
97
|
}
|
|
106
98
|
|
|
@@ -111,14 +103,12 @@ export class StreamrNode extends EventEmitter<Events> {
|
|
|
111
103
|
logger.trace('Destroying StreamrNode...')
|
|
112
104
|
this.destroyed = true
|
|
113
105
|
this.streamParts.forEach((stream) => stream.stop())
|
|
114
|
-
await this.streamPartEntryPointDiscovery!.destroy()
|
|
115
106
|
this.streamParts.clear()
|
|
116
107
|
this.removeAllListeners()
|
|
117
108
|
await this.layer0!.stop()
|
|
118
109
|
await this.P2PTransport!.stop()
|
|
119
110
|
this.layer0 = undefined
|
|
120
111
|
this.P2PTransport = undefined
|
|
121
|
-
this.streamPartEntryPointDiscovery = undefined
|
|
122
112
|
this.connectionLocker = undefined
|
|
123
113
|
}
|
|
124
114
|
|
|
@@ -136,7 +126,6 @@ export class StreamrNode extends EventEmitter<Events> {
|
|
|
136
126
|
stream.stop()
|
|
137
127
|
this.streamParts.delete(streamPartId)
|
|
138
128
|
}
|
|
139
|
-
this.streamPartEntryPointDiscovery!.removeSelfAsEntryPoint(streamPartId)
|
|
140
129
|
}
|
|
141
130
|
|
|
142
131
|
joinStreamPart(streamPartId: StreamPartID): void {
|
|
@@ -147,12 +136,28 @@ export class StreamrNode extends EventEmitter<Events> {
|
|
|
147
136
|
}
|
|
148
137
|
const layer1 = this.createLayer1Node(streamPartId, this.knownStreamPartEntryPoints.get(streamPartId) ?? [])
|
|
149
138
|
const node = this.createRandomGraphNode(streamPartId, layer1)
|
|
139
|
+
const entryPointDiscovery = new StreamPartEntryPointDiscovery({
|
|
140
|
+
streamPartId,
|
|
141
|
+
ownPeerDescriptor: this.getPeerDescriptor(),
|
|
142
|
+
layer1,
|
|
143
|
+
getEntryPointData: (key) => this.layer0!.getDataFromDht(key),
|
|
144
|
+
getEntryPointDataViaNode: (key, node) => this.layer0!.findDataViaPeer(key, node),
|
|
145
|
+
storeEntryPointData: (key, data) => this.layer0!.storeDataToDht(key, data),
|
|
146
|
+
deleteEntryPointData: async (key) => {
|
|
147
|
+
if (this.destroyed) {
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
return this.layer0!.deleteDataFromDht(key)
|
|
151
|
+
}
|
|
152
|
+
})
|
|
150
153
|
stream = {
|
|
151
154
|
proxied: false,
|
|
152
155
|
layer1,
|
|
153
156
|
node,
|
|
157
|
+
entryPointDiscovery,
|
|
154
158
|
broadcast: (msg: StreamMessage) => node.broadcast(msg),
|
|
155
159
|
stop: () => {
|
|
160
|
+
entryPointDiscovery.destroy()
|
|
156
161
|
node.stop()
|
|
157
162
|
layer1.stop()
|
|
158
163
|
}
|
|
@@ -163,14 +168,14 @@ export class StreamrNode extends EventEmitter<Events> {
|
|
|
163
168
|
})
|
|
164
169
|
setImmediate(async () => {
|
|
165
170
|
try {
|
|
166
|
-
await this.startLayersAndJoinDht(streamPartId)
|
|
171
|
+
await this.startLayersAndJoinDht(streamPartId, entryPointDiscovery)
|
|
167
172
|
} catch (err) {
|
|
168
173
|
logger.warn(`Failed to join to stream ${streamPartId} with error: ${err}`)
|
|
169
174
|
}
|
|
170
175
|
})
|
|
171
176
|
}
|
|
172
177
|
|
|
173
|
-
private async startLayersAndJoinDht(streamPartId: StreamPartID): Promise<void> {
|
|
178
|
+
private async startLayersAndJoinDht(streamPartId: StreamPartID, entryPointDiscovery: StreamPartEntryPointDiscovery): Promise<void> {
|
|
174
179
|
logger.debug(`Start layers and join DHT for stream part ${streamPartId}`)
|
|
175
180
|
const stream = this.streamParts.get(streamPartId)
|
|
176
181
|
if ((stream === undefined) || stream.proxied) {
|
|
@@ -181,18 +186,15 @@ export class StreamrNode extends EventEmitter<Events> {
|
|
|
181
186
|
await stream.node.start()
|
|
182
187
|
let entryPoints = this.knownStreamPartEntryPoints.get(streamPartId) ?? []
|
|
183
188
|
const forwardingNode = this.layer0!.isJoinOngoing() ? this.layer0!.getKnownEntryPoints()[0] : undefined
|
|
184
|
-
const discoveryResult = await
|
|
185
|
-
streamPartId,
|
|
189
|
+
const discoveryResult = await entryPointDiscovery.discoverEntryPointsFromDht(
|
|
186
190
|
entryPoints.length,
|
|
187
191
|
forwardingNode
|
|
188
192
|
)
|
|
189
193
|
entryPoints = entryPoints.concat(discoveryResult.discoveredEntryPoints)
|
|
190
194
|
await stream.layer1.joinDht(sampleSize(entryPoints, NETWORK_SPLIT_AVOIDANCE_LIMIT))
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
entryPoints.length
|
|
195
|
-
)
|
|
195
|
+
if (discoveryResult.entryPointsFromDht) {
|
|
196
|
+
await entryPointDiscovery.storeSelfAsEntryPointIfNecessary(entryPoints.length)
|
|
197
|
+
}
|
|
196
198
|
}
|
|
197
199
|
|
|
198
200
|
private createLayer1Node = (streamPartId: StreamPartID, entryPoints: PeerDescriptor[]): ILayer1 => {
|
|
@@ -45,7 +45,7 @@ const createConfigWithDefaults = (config: RandomGraphNodeConfig): StrictRandomGr
|
|
|
45
45
|
const propagation = config.propagation ?? new Propagation({
|
|
46
46
|
minPropagationTargets,
|
|
47
47
|
sendToNeighbor: async (neighborId: NodeID, msg: StreamMessage): Promise<void> => {
|
|
48
|
-
const remote = targetNeighbors.
|
|
48
|
+
const remote = targetNeighbors.get(neighborId) ?? temporaryConnectionServer.getNodes().get(neighborId)
|
|
49
49
|
const proxyConnection = proxyConnectionServer?.getConnection(neighborId)
|
|
50
50
|
if (remote) {
|
|
51
51
|
await remote.sendStreamMessage(msg)
|
|
@@ -9,8 +9,8 @@ interface FindNeighborsSessionConfig {
|
|
|
9
9
|
N: number
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const
|
|
13
|
-
const
|
|
12
|
+
const INITIAL_WAIT = 100
|
|
13
|
+
const INTERVAL = 250
|
|
14
14
|
|
|
15
15
|
export interface INeighborFinder {
|
|
16
16
|
start(excluded?: NodeID[]): void
|
|
@@ -34,7 +34,7 @@ export class NeighborFinder implements INeighborFinder {
|
|
|
34
34
|
}
|
|
35
35
|
const newExcludes = await this.config.doFindNeighbors(excluded)
|
|
36
36
|
if (this.config.targetNeighbors.size() < this.config.N && newExcludes.length < this.config.nearbyNodeView.size()) {
|
|
37
|
-
setAbortableTimeout(() => this.findNeighbors(newExcludes),
|
|
37
|
+
setAbortableTimeout(() => this.findNeighbors(newExcludes), INTERVAL, this.abortController.signal)
|
|
38
38
|
} else {
|
|
39
39
|
this.running = false
|
|
40
40
|
}
|
|
@@ -49,7 +49,7 @@ export class NeighborFinder implements INeighborFinder {
|
|
|
49
49
|
return
|
|
50
50
|
}
|
|
51
51
|
this.running = true
|
|
52
|
-
setAbortableTimeout(() => this.findNeighbors(excluded),
|
|
52
|
+
setAbortableTimeout(() => this.findNeighbors(excluded), INITIAL_WAIT, this.abortController.signal)
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
stop(): void {
|
|
@@ -50,8 +50,8 @@ export class NeighborUpdateManager implements INeighborUpdateManager {
|
|
|
50
50
|
|
|
51
51
|
private async updateNeighborInfo(): Promise<void> {
|
|
52
52
|
logger.trace(`Updating neighbor info to nodes`)
|
|
53
|
-
const neighborDescriptors = this.config.targetNeighbors.
|
|
54
|
-
await Promise.allSettled(this.config.targetNeighbors.
|
|
53
|
+
const neighborDescriptors = this.config.targetNeighbors.getAll().map((neighbor) => neighbor.getPeerDescriptor())
|
|
54
|
+
await Promise.allSettled(this.config.targetNeighbors.getAll().map(async (neighbor) => {
|
|
55
55
|
const res = await this.createRemote(neighbor.getPeerDescriptor()).updateNeighbors(neighborDescriptors)
|
|
56
56
|
if (res.removeMe) {
|
|
57
57
|
this.config.targetNeighbors.remove(neighbor.getPeerDescriptor())
|
|
@@ -49,14 +49,14 @@ export class NeighborUpdateManagerServer implements INeighborUpdateRpc {
|
|
|
49
49
|
this.config.neighborFinder.start()
|
|
50
50
|
const response: NeighborUpdate = {
|
|
51
51
|
randomGraphId: this.config.randomGraphId,
|
|
52
|
-
neighborDescriptors: this.config.targetNeighbors.
|
|
52
|
+
neighborDescriptors: this.config.targetNeighbors.getAll().map((neighbor) => neighbor.getPeerDescriptor()),
|
|
53
53
|
removeMe: false
|
|
54
54
|
}
|
|
55
55
|
return response
|
|
56
56
|
} else {
|
|
57
57
|
const response: NeighborUpdate = {
|
|
58
58
|
randomGraphId: this.config.randomGraphId,
|
|
59
|
-
neighborDescriptors: this.config.targetNeighbors.
|
|
59
|
+
neighborDescriptors: this.config.targetNeighbors.getAll().map((neighbor) => neighbor.getPeerDescriptor()),
|
|
60
60
|
removeMe: true
|
|
61
61
|
}
|
|
62
62
|
return response
|
|
@@ -83,7 +83,7 @@ export class ProxyStreamConnectionClient extends EventEmitter {
|
|
|
83
83
|
markAndCheckDuplicate: (msg: MessageID, prev?: MessageRef) => markAndCheckDuplicate(this.duplicateDetectors, msg, prev),
|
|
84
84
|
broadcast: (message: StreamMessage, previousNode?: NodeID) => this.broadcast(message, previousNode),
|
|
85
85
|
onLeaveNotice: (senderId: NodeID) => {
|
|
86
|
-
const contact = this.targetNeighbors.
|
|
86
|
+
const contact = this.targetNeighbors.get(senderId)
|
|
87
87
|
if (contact) {
|
|
88
88
|
setImmediate(() => this.onNodeDisconnected(contact.getPeerDescriptor()))
|
|
89
89
|
}
|
|
@@ -94,7 +94,7 @@ export class ProxyStreamConnectionClient extends EventEmitter {
|
|
|
94
94
|
this.propagation = new Propagation({
|
|
95
95
|
minPropagationTargets: config.minPropagationTargets ?? 2,
|
|
96
96
|
sendToNeighbor: async (neighborId: NodeID, msg: StreamMessage): Promise<void> => {
|
|
97
|
-
const remote = this.targetNeighbors.
|
|
97
|
+
const remote = this.targetNeighbors.get(neighborId)
|
|
98
98
|
if (remote) {
|
|
99
99
|
await remote.sendStreamMessage(msg)
|
|
100
100
|
} else {
|
|
@@ -202,7 +202,7 @@ export class ProxyStreamConnectionClient extends EventEmitter {
|
|
|
202
202
|
logger.info('Close proxy connection', {
|
|
203
203
|
nodeId
|
|
204
204
|
})
|
|
205
|
-
const server = this.targetNeighbors.
|
|
205
|
+
const server = this.targetNeighbors.get(nodeId)
|
|
206
206
|
server?.leaveStreamPartNotice()
|
|
207
207
|
this.removeConnection(nodeId)
|
|
208
208
|
}
|
|
@@ -221,11 +221,7 @@ export class ProxyStreamConnectionClient extends EventEmitter {
|
|
|
221
221
|
this.propagation.feedUnseenMessage(msg, this.targetNeighbors.getIds(), previousNode ?? null)
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
|
|
225
|
-
return this.targetNeighbors.getIds()
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
hasProxyConnection(nodeId: NodeID, direction: ProxyDirection): boolean {
|
|
224
|
+
hasConnection(nodeId: NodeID, direction: ProxyDirection): boolean {
|
|
229
225
|
return this.connections.has(nodeId) && this.connections.get(nodeId) === direction
|
|
230
226
|
}
|
|
231
227
|
|
|
@@ -233,7 +229,7 @@ export class ProxyStreamConnectionClient extends EventEmitter {
|
|
|
233
229
|
return this.definition!.direction
|
|
234
230
|
}
|
|
235
231
|
|
|
236
|
-
async onNodeDisconnected(peerDescriptor: PeerDescriptor): Promise<void> {
|
|
232
|
+
private async onNodeDisconnected(peerDescriptor: PeerDescriptor): Promise<void> {
|
|
237
233
|
const nodeId = getNodeIdFromPeerDescriptor(peerDescriptor)
|
|
238
234
|
if (this.connections.has(nodeId)) {
|
|
239
235
|
this.config.connectionLocker.unlockConnection(peerDescriptor, 'proxy-stream-connection-client')
|
|
@@ -253,7 +249,7 @@ export class ProxyStreamConnectionClient extends EventEmitter {
|
|
|
253
249
|
}
|
|
254
250
|
|
|
255
251
|
stop(): void {
|
|
256
|
-
this.targetNeighbors.
|
|
252
|
+
this.targetNeighbors.getAll().map((remote) => {
|
|
257
253
|
this.config.connectionLocker.unlockConnection(remote.getPeerDescriptor(), 'proxy-stream-connection-client')
|
|
258
254
|
remote.leaveStreamPartNotice()
|
|
259
255
|
})
|
|
@@ -66,14 +66,6 @@ export class ProxyStreamConnectionServer extends EventEmitter<Events> implements
|
|
|
66
66
|
this.removeAllListeners()
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
getConnectedNodeIds(): NodeID[] {
|
|
70
|
-
return Array.from(this.connections.keys())
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
getConnections(): ProxyConnection[] {
|
|
74
|
-
return Array.from(this.connections.values())
|
|
75
|
-
}
|
|
76
|
-
|
|
77
69
|
getPropagationTargets(msg: StreamMessage): NodeID[] {
|
|
78
70
|
if (msg.messageType === StreamMessageType.GROUP_KEY_REQUEST) {
|
|
79
71
|
try {
|
|
@@ -42,7 +42,7 @@ describe('Proxy connections', () => {
|
|
|
42
42
|
|
|
43
43
|
const hasConnectionToProxy = (proxyNodeId: NodeID, direction: ProxyDirection): boolean => {
|
|
44
44
|
const client = (proxiedNode.stack.getStreamrNode()!.getStream(STREAM_PART_ID) as { client: ProxyStreamConnectionClient }).client
|
|
45
|
-
return client.
|
|
45
|
+
return client.hasConnection(proxyNodeId, direction)
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
beforeEach(async () => {
|
|
@@ -60,7 +60,7 @@ describe('Full node network with WebSocket connections only', () => {
|
|
|
60
60
|
it('happy path', async () => {
|
|
61
61
|
await Promise.all(nodes.map((node) =>
|
|
62
62
|
waitForCondition(() => {
|
|
63
|
-
return node.getStreamrNode()!.getNeighbors(randomGraphId).length >=
|
|
63
|
+
return node.getStreamrNode()!.getNeighbors(randomGraphId).length >= 4
|
|
64
64
|
}
|
|
65
65
|
, 120000)
|
|
66
66
|
))
|
|
@@ -68,8 +68,8 @@ describe('RandomGraphNode', () => {
|
|
|
68
68
|
const peerDescriptor2 = createMockPeerDescriptor()
|
|
69
69
|
layer1.emit('newContact', peerDescriptor1, [peerDescriptor1, peerDescriptor2])
|
|
70
70
|
await waitForCondition(() => nearbyNodeView.size() === 2)
|
|
71
|
-
expect(nearbyNodeView.
|
|
72
|
-
expect(nearbyNodeView.
|
|
71
|
+
expect(nearbyNodeView.get(getNodeIdFromPeerDescriptor(peerDescriptor1))).toBeTruthy()
|
|
72
|
+
expect(nearbyNodeView.get(getNodeIdFromPeerDescriptor(peerDescriptor2))).toBeTruthy()
|
|
73
73
|
})
|
|
74
74
|
|
|
75
75
|
it('Adds Random Nodes from layer1 newRandomContact event to randomNodeView', async () => {
|
|
@@ -77,8 +77,8 @@ describe('RandomGraphNode', () => {
|
|
|
77
77
|
const peerDescriptor2 = createMockPeerDescriptor()
|
|
78
78
|
layer1.emit('newRandomContact', peerDescriptor1, [peerDescriptor1, peerDescriptor2])
|
|
79
79
|
await waitForCondition(() => randomNodeView.size() === 2)
|
|
80
|
-
expect(randomNodeView.
|
|
81
|
-
expect(randomNodeView.
|
|
80
|
+
expect(randomNodeView.get(getNodeIdFromPeerDescriptor(peerDescriptor1))).toBeTruthy()
|
|
81
|
+
expect(randomNodeView.get(getNodeIdFromPeerDescriptor(peerDescriptor2))).toBeTruthy()
|
|
82
82
|
})
|
|
83
83
|
|
|
84
84
|
it('Adds Nodes from layer1 KBucket to nearbyNodeView if its size is below nodeViewSize', async () => {
|
|
@@ -87,8 +87,8 @@ describe('RandomGraphNode', () => {
|
|
|
87
87
|
layer1.addNewRandomPeerToKBucket()
|
|
88
88
|
layer1.emit('newContact', peerDescriptor1, [peerDescriptor1, peerDescriptor2])
|
|
89
89
|
await waitForCondition(() => nearbyNodeView.size() === 3)
|
|
90
|
-
expect(nearbyNodeView.
|
|
91
|
-
expect(nearbyNodeView.
|
|
90
|
+
expect(nearbyNodeView.get(getNodeIdFromPeerDescriptor(peerDescriptor1))).toBeTruthy()
|
|
91
|
+
expect(nearbyNodeView.get(getNodeIdFromPeerDescriptor(peerDescriptor2))).toBeTruthy()
|
|
92
92
|
})
|
|
93
93
|
|
|
94
94
|
})
|