@streamr/trackerless-network 0.0.1-tatum.5 → 0.0.1-tatum.7
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 +10 -8
- package/dist/src/NetworkNode.js +1 -1
- package/dist/src/NetworkNode.js.map +1 -1
- package/dist/src/NetworkStack.d.ts +1 -3
- package/dist/src/NetworkStack.js +18 -47
- package/dist/src/NetworkStack.js.map +1 -1
- package/dist/src/identifiers.js +2 -2
- package/dist/src/identifiers.js.map +1 -1
- package/dist/src/logic/{StreamNodeServer.d.ts → DeliveryRpcLocal.d.ts} +6 -5
- package/dist/src/logic/{StreamNodeServer.js → DeliveryRpcLocal.js} +5 -5
- package/dist/src/logic/{StreamNodeServer.js.map → DeliveryRpcLocal.js.map} +1 -1
- package/dist/src/logic/{RemoteRandomGraphNode.d.ts → DeliveryRpcRemote.d.ts} +2 -2
- package/dist/src/logic/{RemoteRandomGraphNode.js → DeliveryRpcRemote.js} +5 -5
- package/dist/src/logic/DeliveryRpcRemote.js.map +1 -0
- package/dist/src/logic/EntryPointDiscovery.d.ts +35 -0
- package/dist/src/logic/EntryPointDiscovery.js +145 -0
- package/dist/src/logic/EntryPointDiscovery.js.map +1 -0
- package/dist/src/logic/ILayer0.d.ts +3 -6
- package/dist/src/logic/ILayer1.d.ts +2 -2
- package/dist/src/logic/NodeList.d.ts +10 -10
- package/dist/src/logic/NodeList.js +2 -2
- package/dist/src/logic/NodeList.js.map +1 -1
- package/dist/src/logic/RandomGraphNode.d.ts +8 -12
- package/dist/src/logic/RandomGraphNode.js +47 -46
- package/dist/src/logic/RandomGraphNode.js.map +1 -1
- package/dist/src/logic/StreamrNode.d.ts +9 -11
- package/dist/src/logic/StreamrNode.js +72 -75
- package/dist/src/logic/StreamrNode.js.map +1 -1
- package/dist/src/logic/createRandomGraphNode.d.ts +6 -1
- package/dist/src/logic/createRandomGraphNode.js +17 -23
- package/dist/src/logic/createRandomGraphNode.js.map +1 -1
- package/dist/src/logic/formStreamPartDeliveryServiceId.d.ts +2 -0
- package/dist/src/logic/formStreamPartDeliveryServiceId.js +8 -0
- package/dist/src/logic/formStreamPartDeliveryServiceId.js.map +1 -0
- package/dist/src/logic/inspect/Inspector.d.ts +3 -2
- package/dist/src/logic/inspect/Inspector.js +5 -5
- package/dist/src/logic/inspect/Inspector.js.map +1 -1
- package/dist/src/logic/neighbor-discovery/{HandshakerServer.d.ts → HandshakeRpcLocal.d.ts} +10 -10
- package/dist/src/logic/neighbor-discovery/{HandshakerServer.js → HandshakeRpcLocal.js} +13 -13
- package/dist/src/logic/neighbor-discovery/HandshakeRpcLocal.js.map +1 -0
- package/dist/src/logic/neighbor-discovery/{RemoteHandshaker.d.ts → HandshakeRpcRemote.d.ts} +1 -1
- package/dist/src/logic/neighbor-discovery/{RemoteHandshaker.js → HandshakeRpcRemote.js} +8 -7
- package/dist/src/logic/neighbor-discovery/HandshakeRpcRemote.js.map +1 -0
- package/dist/src/logic/neighbor-discovery/Handshaker.d.ts +8 -7
- package/dist/src/logic/neighbor-discovery/Handshaker.js +23 -24
- package/dist/src/logic/neighbor-discovery/Handshaker.js.map +1 -1
- package/dist/src/logic/neighbor-discovery/NeighborFinder.d.ts +1 -1
- package/dist/src/logic/neighbor-discovery/NeighborFinder.js +5 -5
- package/dist/src/logic/neighbor-discovery/NeighborFinder.js.map +1 -1
- package/dist/src/logic/neighbor-discovery/NeighborUpdateManager.d.ts +3 -2
- package/dist/src/logic/neighbor-discovery/NeighborUpdateManager.js +7 -7
- package/dist/src/logic/neighbor-discovery/NeighborUpdateManager.js.map +1 -1
- package/dist/src/logic/neighbor-discovery/{NeighborUpdateManagerServer.d.ts → NeighborUpdateRpcLocal.d.ts} +6 -6
- package/dist/src/logic/neighbor-discovery/{NeighborUpdateManagerServer.js → NeighborUpdateRpcLocal.js} +11 -11
- package/dist/src/logic/neighbor-discovery/NeighborUpdateRpcLocal.js.map +1 -0
- package/dist/src/logic/neighbor-discovery/{RemoteNeighborUpdateManager.d.ts → NeighborUpdateRpcRemote.d.ts} +1 -1
- package/dist/src/logic/neighbor-discovery/{RemoteNeighborUpdateManager.js → NeighborUpdateRpcRemote.js} +5 -5
- package/dist/src/logic/neighbor-discovery/NeighborUpdateRpcRemote.js.map +1 -0
- package/dist/src/logic/propagation/Propagation.js +2 -2
- package/dist/src/logic/propagation/Propagation.js.map +1 -1
- package/dist/src/logic/proxy/{ProxyStreamConnectionClient.d.ts → ProxyClient.d.ts} +8 -12
- package/dist/src/logic/proxy/{ProxyStreamConnectionClient.js → ProxyClient.js} +28 -29
- package/dist/src/logic/proxy/ProxyClient.js.map +1 -0
- package/dist/src/logic/proxy/{ProxyStreamConnectionServer.d.ts → ProxyConnectionRpcLocal.d.ts} +5 -7
- package/dist/src/logic/proxy/{ProxyStreamConnectionServer.js → ProxyConnectionRpcLocal.js} +6 -12
- package/dist/src/logic/proxy/ProxyConnectionRpcLocal.js.map +1 -0
- package/dist/src/logic/proxy/{RemoteProxyServer.d.ts → ProxyConnectionRpcRemote.d.ts} +1 -1
- package/dist/src/logic/proxy/{RemoteProxyServer.js → ProxyConnectionRpcRemote.js} +4 -4
- package/dist/src/logic/proxy/ProxyConnectionRpcRemote.js.map +1 -0
- package/dist/src/logic/temporary-connection/{TemporaryConnectionRpcServer.d.ts → TemporaryConnectionRpcLocal.d.ts} +5 -4
- package/dist/src/logic/temporary-connection/{TemporaryConnectionRpcServer.js → TemporaryConnectionRpcLocal.js} +6 -6
- package/dist/src/logic/temporary-connection/TemporaryConnectionRpcLocal.js.map +1 -0
- package/dist/src/logic/temporary-connection/{RemoteTemporaryConnectionRpcServer.d.ts → TemporaryConnectionRpcRemote.d.ts} +1 -1
- package/dist/src/logic/temporary-connection/{RemoteTemporaryConnectionRpcServer.js → TemporaryConnectionRpcRemote.js} +4 -4
- package/dist/src/logic/temporary-connection/TemporaryConnectionRpcRemote.js.map +1 -0
- package/dist/src/proto/packages/dht/protos/DhtRpc.d.ts +0 -4
- package/dist/src/proto/packages/dht/protos/DhtRpc.js +1 -2
- package/dist/src/proto/packages/dht/protos/DhtRpc.js.map +1 -1
- package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.client.d.ts +4 -4
- package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.client.js +7 -7
- package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.client.js.map +1 -1
- package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.d.ts +10 -10
- package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.js +7 -7
- package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.js.map +1 -1
- package/dist/src/proto/packages/trackerless-network/protos/NetworkRpc.server.d.ts +2 -2
- package/dist/test/benchmark/first-message.js +10 -13
- package/dist/test/benchmark/first-message.js.map +1 -1
- package/dist/test/utils/utils.d.ts +5 -5
- package/dist/test/utils/utils.js +14 -14
- package/dist/test/utils/utils.js.map +1 -1
- package/package.json +10 -8
- package/protos/NetworkRpc.proto +5 -5
- package/src/NetworkNode.ts +1 -1
- package/src/NetworkStack.ts +20 -62
- package/src/identifiers.ts +3 -3
- package/src/logic/{StreamNodeServer.ts → DeliveryRpcLocal.ts} +8 -7
- package/src/logic/{RemoteRandomGraphNode.ts → DeliveryRpcRemote.ts} +3 -3
- package/src/logic/EntryPointDiscovery.ts +181 -0
- package/src/logic/ILayer0.ts +3 -6
- package/src/logic/ILayer1.ts +2 -5
- package/src/logic/NodeList.ts +12 -12
- package/src/logic/RandomGraphNode.ts +67 -69
- package/src/logic/StreamrNode.ts +78 -90
- package/src/logic/createRandomGraphNode.ts +28 -26
- package/src/logic/formStreamPartDeliveryServiceId.ts +5 -0
- package/src/logic/inspect/Inspector.ts +8 -7
- package/src/logic/neighbor-discovery/{HandshakerServer.ts → HandshakeRpcLocal.ts} +20 -20
- package/src/logic/neighbor-discovery/{RemoteHandshaker.ts → HandshakeRpcRemote.ts} +6 -5
- package/src/logic/neighbor-discovery/Handshaker.ts +38 -38
- package/src/logic/neighbor-discovery/NeighborFinder.ts +6 -6
- package/src/logic/neighbor-discovery/NeighborUpdateManager.ts +15 -12
- package/src/logic/neighbor-discovery/{NeighborUpdateManagerServer.ts → NeighborUpdateRpcLocal.ts} +17 -17
- package/src/logic/neighbor-discovery/{RemoteNeighborUpdateManager.ts → NeighborUpdateRpcRemote.ts} +2 -2
- package/src/logic/propagation/Propagation.ts +2 -2
- package/src/logic/proxy/{ProxyStreamConnectionClient.ts → ProxyClient.ts} +33 -37
- package/src/logic/proxy/{ProxyStreamConnectionServer.ts → ProxyConnectionRpcLocal.ts} +10 -19
- package/src/logic/proxy/{RemoteProxyServer.ts → ProxyConnectionRpcRemote.ts} +1 -1
- package/src/logic/temporary-connection/{TemporaryConnectionRpcServer.ts → TemporaryConnectionRpcLocal.ts} +11 -10
- package/src/logic/temporary-connection/{RemoteTemporaryConnectionRpcServer.ts → TemporaryConnectionRpcRemote.ts} +1 -1
- package/src/proto/packages/dht/protos/DhtRpc.ts +1 -6
- package/src/proto/packages/trackerless-network/protos/NetworkRpc.client.ts +8 -8
- package/src/proto/packages/trackerless-network/protos/NetworkRpc.server.ts +2 -2
- package/src/proto/packages/trackerless-network/protos/NetworkRpc.ts +14 -14
- package/test/benchmark/first-message.ts +10 -13
- package/test/end-to-end/inspect.test.ts +12 -12
- package/test/end-to-end/proxy-and-full-node.test.ts +13 -14
- package/test/end-to-end/proxy-connections.test.ts +10 -12
- package/test/end-to-end/proxy-key-exchange.test.ts +12 -13
- package/test/end-to-end/random-graph-with-real-connections.test.ts +7 -7
- package/test/end-to-end/webrtc-full-node-network.test.ts +8 -8
- package/test/end-to-end/websocket-full-node-network.test.ts +8 -10
- package/test/integration/{RemoteRandomGraphNode.test.ts → DeliveryRpcRemote.test.ts} +17 -14
- package/test/integration/{RemoteHandshaker.test.ts → HandshakeRpcRemote.test.ts} +10 -9
- package/test/integration/Handshakes.test.ts +23 -20
- package/test/integration/Inspect.test.ts +3 -2
- package/test/integration/{RemoteNeighborUpdateManager.test.ts → NeighborUpdateRpcRemote.test.ts} +12 -10
- package/test/integration/NetworkNode.test.ts +9 -8
- package/test/integration/NetworkRpc.test.ts +5 -7
- package/test/integration/NetworkStack.test.ts +13 -15
- package/test/integration/Propagation.test.ts +2 -2
- package/test/integration/RandomGraphNode-Layer1Node-Latencies.test.ts +10 -8
- package/test/integration/RandomGraphNode-Layer1Node.test.ts +17 -17
- package/test/integration/StreamrNode.test.ts +5 -3
- package/test/integration/joining-streams-on-offline-peers.test.ts +16 -18
- package/test/integration/stream-without-default-entrypoints.test.ts +11 -13
- package/test/unit/{StreamNodeServer.test.ts → DeliveryRpcLocal.test.ts} +8 -8
- package/test/unit/EntrypointDiscovery.test.ts +132 -0
- package/test/unit/{HandshakerServer.test.ts → HandshakeRpcLocal.test.ts} +26 -24
- package/test/unit/Handshaker.test.ts +10 -8
- package/test/unit/Inspector.test.ts +4 -3
- package/test/unit/NeighborFinder.test.ts +5 -5
- package/test/unit/NodeList.test.ts +22 -13
- package/test/unit/{RemoteProxyServer.test.ts → ProxyConnectionRpcRemote.test.ts} +4 -4
- package/test/unit/RandomGraphNode.test.ts +12 -11
- package/test/unit/StreamMessageTranslator.test.ts +10 -9
- package/test/unit/StreamrNode.test.ts +8 -8
- package/test/utils/mock/MockLayer0.ts +7 -26
- package/test/utils/mock/MockLayer1.ts +6 -13
- package/test/utils/mock/MockNeighborFinder.ts +1 -2
- package/test/utils/mock/MockNeighborUpdateManager.ts +1 -2
- package/test/utils/mock/Transport.ts +2 -2
- package/test/utils/utils.ts +13 -13
- package/dist/src/logic/RemoteRandomGraphNode.js.map +0 -1
- package/dist/src/logic/StreamPartEntryPointDiscovery.d.ts +0 -39
- package/dist/src/logic/StreamPartEntryPointDiscovery.js +0 -194
- package/dist/src/logic/StreamPartEntryPointDiscovery.js.map +0 -1
- package/dist/src/logic/neighbor-discovery/HandshakerServer.js.map +0 -1
- package/dist/src/logic/neighbor-discovery/NeighborUpdateManagerServer.js.map +0 -1
- package/dist/src/logic/neighbor-discovery/RemoteHandshaker.js.map +0 -1
- package/dist/src/logic/neighbor-discovery/RemoteNeighborUpdateManager.js.map +0 -1
- package/dist/src/logic/proxy/ProxyStreamConnectionClient.js.map +0 -1
- package/dist/src/logic/proxy/ProxyStreamConnectionServer.js.map +0 -1
- package/dist/src/logic/proxy/RemoteProxyServer.js.map +0 -1
- package/dist/src/logic/temporary-connection/RemoteTemporaryConnectionRpcServer.js.map +0 -1
- package/dist/src/logic/temporary-connection/TemporaryConnectionRpcServer.js.map +0 -1
- package/src/logic/StreamPartEntryPointDiscovery.ts +0 -240
- package/test/unit/StreamPartEntrypointDiscovery.test.ts +0 -164
package/src/NetworkStack.ts
CHANGED
|
@@ -1,40 +1,10 @@
|
|
|
1
|
-
import { ConnectionManager, DhtNode, DhtNodeOptions,
|
|
1
|
+
import { ConnectionManager, DhtNode, DhtNodeOptions, areEqualPeerDescriptors } from '@streamr/dht'
|
|
2
2
|
import { StreamrNode, StreamrNodeConfig } from './logic/StreamrNode'
|
|
3
|
-
import { MetricsContext, waitForCondition
|
|
3
|
+
import { MetricsContext, waitForCondition } from '@streamr/utils'
|
|
4
4
|
import { EventEmitter } from 'eventemitter3'
|
|
5
5
|
import { StreamID, StreamPartID, toStreamPartID } from '@streamr/protocol'
|
|
6
6
|
import { ProxyDirection, StreamMessage, StreamMessageType } from './proto/packages/trackerless-network/protos/NetworkRpc'
|
|
7
7
|
|
|
8
|
-
interface ReadinessEvents {
|
|
9
|
-
done: () => void
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
class ReadinessListener {
|
|
13
|
-
|
|
14
|
-
private readonly emitter = new EventEmitter<ReadinessEvents>()
|
|
15
|
-
private readonly networkStack: NetworkStack
|
|
16
|
-
private readonly dhtNode: DhtNode
|
|
17
|
-
|
|
18
|
-
constructor(networkStack: NetworkStack, dhtNode: DhtNode) {
|
|
19
|
-
this.networkStack = networkStack
|
|
20
|
-
this.dhtNode = dhtNode
|
|
21
|
-
this.networkStack.on('stopped', this.onDone)
|
|
22
|
-
this.dhtNode.on('connected', this.onDone)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
private onDone = () => {
|
|
26
|
-
this.networkStack.off('stopped', this.onDone)
|
|
27
|
-
this.dhtNode.off('connected', this.onDone)
|
|
28
|
-
this.emitter.emit('done')
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
public async waitUntilReady(timeout: number): Promise<void> {
|
|
32
|
-
if (this.dhtNode.getNumberOfConnections() === 0) {
|
|
33
|
-
await waitForEvent3<ReadinessEvents>(this.emitter, 'done', timeout)
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
8
|
export interface NetworkOptions {
|
|
39
9
|
layer0?: DhtNodeOptions
|
|
40
10
|
networkNode?: StreamrNodeConfig
|
|
@@ -45,8 +15,6 @@ export interface NetworkStackEvents {
|
|
|
45
15
|
stopped: () => void
|
|
46
16
|
}
|
|
47
17
|
|
|
48
|
-
const DEFAULT_FIRST_CONNECTION_TIMEOUT = 5000
|
|
49
|
-
|
|
50
18
|
export class NetworkStack extends EventEmitter<NetworkStackEvents> {
|
|
51
19
|
|
|
52
20
|
private layer0DhtNode?: DhtNode
|
|
@@ -64,7 +32,6 @@ export class NetworkStack extends EventEmitter<NetworkStackEvents> {
|
|
|
64
32
|
})
|
|
65
33
|
this.streamrNode = new StreamrNode({
|
|
66
34
|
...options.networkNode,
|
|
67
|
-
nodeName: options.networkNode?.nodeName ?? options.layer0?.nodeName,
|
|
68
35
|
metricsContext: this.metricsContext
|
|
69
36
|
})
|
|
70
37
|
}
|
|
@@ -73,7 +40,7 @@ export class NetworkStack extends EventEmitter<NetworkStackEvents> {
|
|
|
73
40
|
if (this.getStreamrNode().isProxiedStreamPart(streamPartId)) {
|
|
74
41
|
throw new Error(`Cannot join to ${streamPartId} as proxy connections have been set`)
|
|
75
42
|
}
|
|
76
|
-
await this.
|
|
43
|
+
await this.ensureConnectedToControlLayer()
|
|
77
44
|
this.getStreamrNode().joinStreamPart(streamPartId)
|
|
78
45
|
if (neighborRequirement !== undefined) {
|
|
79
46
|
await waitForCondition(() => {
|
|
@@ -87,7 +54,10 @@ export class NetworkStack extends EventEmitter<NetworkStackEvents> {
|
|
|
87
54
|
if (this.getStreamrNode().isProxiedStreamPart(streamPartId, ProxyDirection.SUBSCRIBE) && (msg.messageType === StreamMessageType.MESSAGE)) {
|
|
88
55
|
throw new Error(`Cannot broadcast to ${streamPartId} as proxy subscribe connections have been set`)
|
|
89
56
|
}
|
|
90
|
-
|
|
57
|
+
// TODO could combine these two calls to isProxiedStreamPart?
|
|
58
|
+
if (!this.streamrNode!.isProxiedStreamPart(streamPartId)) {
|
|
59
|
+
await this.ensureConnectedToControlLayer()
|
|
60
|
+
}
|
|
91
61
|
this.getStreamrNode().broadcast(msg)
|
|
92
62
|
}
|
|
93
63
|
|
|
@@ -95,43 +65,31 @@ export class NetworkStack extends EventEmitter<NetworkStackEvents> {
|
|
|
95
65
|
await this.layer0DhtNode!.start()
|
|
96
66
|
const connectionManager = this.layer0DhtNode!.getTransport() as ConnectionManager
|
|
97
67
|
if ((this.options.layer0?.entryPoints !== undefined) && (this.options.layer0.entryPoints.some((entryPoint) =>
|
|
98
|
-
|
|
68
|
+
areEqualPeerDescriptors(entryPoint, this.layer0DhtNode!.getPeerDescriptor())
|
|
99
69
|
))) {
|
|
100
70
|
await this.layer0DhtNode?.joinDht(this.options.layer0.entryPoints)
|
|
101
71
|
} else {
|
|
102
72
|
if (doJoin) {
|
|
103
|
-
|
|
73
|
+
// in practice there aren't be existing connections and therefore this always connects
|
|
74
|
+
await this.ensureConnectedToControlLayer()
|
|
104
75
|
}
|
|
105
76
|
}
|
|
106
77
|
await this.streamrNode?.start(this.layer0DhtNode!, connectionManager, connectionManager)
|
|
107
78
|
}
|
|
108
79
|
|
|
109
|
-
private async
|
|
110
|
-
setImmediate(async () => {
|
|
111
|
-
if (this.options.layer0?.entryPoints !== undefined) {
|
|
112
|
-
// TODO should catch possible rejection?
|
|
113
|
-
await this.layer0DhtNode?.joinDht(this.options.layer0.entryPoints)
|
|
114
|
-
}
|
|
115
|
-
})
|
|
116
|
-
await this.waitForFirstConnection()
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
private async waitForFirstConnection(): Promise<void> {
|
|
120
|
-
const readinessListener = new ReadinessListener(this, this.layer0DhtNode!)
|
|
121
|
-
const timeout = this.options.networkNode?.firstConnectionTimeout ?? DEFAULT_FIRST_CONNECTION_TIMEOUT
|
|
122
|
-
await readinessListener.waitUntilReady(timeout)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
async joinLayer0IfRequired(streamPartId: StreamPartID): Promise<void> {
|
|
126
|
-
if (this.streamrNode!.isProxiedStreamPart(streamPartId)) {
|
|
127
|
-
return
|
|
128
|
-
}
|
|
80
|
+
private async ensureConnectedToControlLayer(): Promise<void> {
|
|
129
81
|
// TODO we could wrap joinDht with pOnce and call it here (no else-if needed in that case)
|
|
130
82
|
if (!this.layer0DhtNode!.hasJoined()) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
83
|
+
setImmediate(async () => {
|
|
84
|
+
if (this.options.layer0?.entryPoints !== undefined) {
|
|
85
|
+
// TODO should catch possible rejection?
|
|
86
|
+
// the question mark is there to avoid problems when stop() is called before start()
|
|
87
|
+
// -> TODO change to exlamation mark if we don't support that (and remove NetworkStackStoppedDuringStart.test)
|
|
88
|
+
await this.layer0DhtNode?.joinDht(this.options.layer0.entryPoints)
|
|
89
|
+
}
|
|
90
|
+
})
|
|
134
91
|
}
|
|
92
|
+
await this.layer0DhtNode!.waitForNetworkConnectivity()
|
|
135
93
|
}
|
|
136
94
|
|
|
137
95
|
getStreamrNode(): StreamrNode {
|
package/src/identifiers.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { PeerDescriptor
|
|
2
|
-
import { BrandedString } from '@streamr/utils'
|
|
1
|
+
import { PeerDescriptor } from '@streamr/dht'
|
|
2
|
+
import { BrandedString, binaryToHex } from '@streamr/utils'
|
|
3
3
|
|
|
4
4
|
export type NodeID = BrandedString<'NodeID'>
|
|
5
5
|
|
|
6
6
|
export const getNodeIdFromPeerDescriptor = (peerDescriptor: PeerDescriptor): NodeID => {
|
|
7
|
-
return
|
|
7
|
+
return binaryToHex(peerDescriptor.kademliaId) as unknown as NodeID
|
|
8
8
|
}
|
|
@@ -6,13 +6,14 @@ import {
|
|
|
6
6
|
MessageRef,
|
|
7
7
|
StreamMessage
|
|
8
8
|
} from '../proto/packages/trackerless-network/protos/NetworkRpc'
|
|
9
|
-
import {
|
|
9
|
+
import { IDeliveryRpc } from '../proto/packages/trackerless-network/protos/NetworkRpc.server'
|
|
10
10
|
import { ServerCallContext } from '@protobuf-ts/runtime-rpc'
|
|
11
11
|
import { NodeID, getNodeIdFromPeerDescriptor } from '../identifiers'
|
|
12
|
+
import { StreamPartID } from '@streamr/protocol'
|
|
12
13
|
|
|
13
|
-
export interface
|
|
14
|
+
export interface DeliveryRpcLocalConfig {
|
|
14
15
|
ownPeerDescriptor: PeerDescriptor
|
|
15
|
-
|
|
16
|
+
streamPartId: StreamPartID
|
|
16
17
|
markAndCheckDuplicate: (messageId: MessageID, previousMessageRef?: MessageRef) => boolean
|
|
17
18
|
broadcast: (message: StreamMessage, previousNode?: NodeID) => void
|
|
18
19
|
onLeaveNotice(senderId: NodeID): void
|
|
@@ -20,11 +21,11 @@ export interface StreamNodeServerConfig {
|
|
|
20
21
|
rpcCommunicator: ListeningRpcCommunicator
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
export class
|
|
24
|
+
export class DeliveryRpcLocal implements IDeliveryRpc {
|
|
24
25
|
|
|
25
|
-
private readonly config:
|
|
26
|
+
private readonly config: DeliveryRpcLocalConfig
|
|
26
27
|
|
|
27
|
-
constructor(config:
|
|
28
|
+
constructor(config: DeliveryRpcLocalConfig) {
|
|
28
29
|
this.config = config
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -38,7 +39,7 @@ export class StreamNodeServer implements INetworkRpc {
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
async leaveStreamPartNotice(message: LeaveStreamPartNotice, context: ServerCallContext): Promise<Empty> {
|
|
41
|
-
if (message.
|
|
42
|
+
if (message.streamPartId === this.config.streamPartId) {
|
|
42
43
|
const senderPeerDescriptor = (context as DhtCallContext).incomingSourceDescriptor!
|
|
43
44
|
const senderId = getNodeIdFromPeerDescriptor(senderPeerDescriptor)
|
|
44
45
|
this.config.onLeaveNotice(senderId)
|
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
LeaveStreamPartNotice,
|
|
5
5
|
StreamMessage
|
|
6
6
|
} from '../proto/packages/trackerless-network/protos/NetworkRpc'
|
|
7
|
-
import {
|
|
7
|
+
import { IDeliveryRpcClient } from '../proto/packages/trackerless-network/protos/NetworkRpc.client'
|
|
8
8
|
|
|
9
9
|
const logger = new Logger(module)
|
|
10
10
|
|
|
11
|
-
export class
|
|
11
|
+
export class DeliveryRpcRemote extends Remote<IDeliveryRpcClient> {
|
|
12
12
|
|
|
13
13
|
async sendStreamMessage(msg: StreamMessage): Promise<void> {
|
|
14
14
|
const options = this.formDhtRpcOptions({
|
|
@@ -21,7 +21,7 @@ export class RemoteRandomGraphNode extends Remote<INetworkRpcClient> {
|
|
|
21
21
|
|
|
22
22
|
leaveStreamPartNotice(): void {
|
|
23
23
|
const notification: LeaveStreamPartNotice = {
|
|
24
|
-
|
|
24
|
+
streamPartId: this.getServiceId()
|
|
25
25
|
}
|
|
26
26
|
const options = this.formDhtRpcOptions({
|
|
27
27
|
notification: true
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DataEntry,
|
|
3
|
+
PeerDescriptor,
|
|
4
|
+
areEqualPeerDescriptors
|
|
5
|
+
} from '@streamr/dht'
|
|
6
|
+
import { StreamPartID } from '@streamr/protocol'
|
|
7
|
+
import { Logger, scheduleAtInterval, wait } from '@streamr/utils'
|
|
8
|
+
import { createHash } from 'crypto'
|
|
9
|
+
import { NodeID, getNodeIdFromPeerDescriptor } from '../identifiers'
|
|
10
|
+
import { Any } from '../proto/google/protobuf/any'
|
|
11
|
+
import { ILayer1 } from './ILayer1'
|
|
12
|
+
|
|
13
|
+
export const streamPartIdToDataKey = (streamPartId: StreamPartID): Uint8Array => {
|
|
14
|
+
return new Uint8Array(createHash('md5').update(streamPartId).digest())
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const parseEntryPointData = (dataEntries: DataEntry[]): PeerDescriptor[] => {
|
|
18
|
+
return dataEntries.filter((entry) => !entry.deleted).map((entry) => Any.unpack(entry.data!, PeerDescriptor))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface FindEntryPointsResult {
|
|
22
|
+
entryPointsFromDht: boolean
|
|
23
|
+
discoveredEntryPoints: PeerDescriptor[]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const exponentialRunOff = async (
|
|
27
|
+
task: () => Promise<void>,
|
|
28
|
+
description: string,
|
|
29
|
+
abortSignal: AbortSignal,
|
|
30
|
+
baseDelay = 500,
|
|
31
|
+
maxAttempts = 6
|
|
32
|
+
): Promise<void> => {
|
|
33
|
+
for (let i = 1; i <= maxAttempts; i++) {
|
|
34
|
+
if (abortSignal.aborted) {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
const factor = 2 ** i
|
|
38
|
+
const delay = baseDelay * factor
|
|
39
|
+
try {
|
|
40
|
+
await task()
|
|
41
|
+
} catch (e: any) {
|
|
42
|
+
logger.trace(`${description} failed, retrying in ${delay} ms`)
|
|
43
|
+
}
|
|
44
|
+
try { // Abort controller throws unexpected errors in destroy?
|
|
45
|
+
await wait(delay, abortSignal)
|
|
46
|
+
} catch (err) {
|
|
47
|
+
logger.trace(`${err}`)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const logger = new Logger(module)
|
|
53
|
+
|
|
54
|
+
const ENTRYPOINT_STORE_LIMIT = 8
|
|
55
|
+
export const NETWORK_SPLIT_AVOIDANCE_LIMIT = 4
|
|
56
|
+
|
|
57
|
+
interface EntryPointDiscoveryConfig {
|
|
58
|
+
streamPartId: StreamPartID
|
|
59
|
+
ownPeerDescriptor: PeerDescriptor
|
|
60
|
+
layer1: ILayer1
|
|
61
|
+
getEntryPointData: (key: Uint8Array) => Promise<DataEntry[]>
|
|
62
|
+
storeEntryPointData: (key: Uint8Array, data: Any) => Promise<PeerDescriptor[]>
|
|
63
|
+
deleteEntryPointData: (key: Uint8Array) => Promise<void>
|
|
64
|
+
storeInterval?: number
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export class EntryPointDiscovery {
|
|
68
|
+
private readonly abortController: AbortController
|
|
69
|
+
private readonly config: EntryPointDiscoveryConfig
|
|
70
|
+
private readonly storeInterval: number
|
|
71
|
+
private readonly networkSplitAvoidedNodes: Set<NodeID> = new Set()
|
|
72
|
+
|
|
73
|
+
constructor(config: EntryPointDiscoveryConfig) {
|
|
74
|
+
this.config = config
|
|
75
|
+
this.abortController = new AbortController()
|
|
76
|
+
this.storeInterval = this.config.storeInterval ?? 60000
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async discoverEntryPointsFromDht(
|
|
80
|
+
knownEntryPointCount: number
|
|
81
|
+
): Promise<FindEntryPointsResult> {
|
|
82
|
+
if (knownEntryPointCount > 0) {
|
|
83
|
+
return {
|
|
84
|
+
entryPointsFromDht: false,
|
|
85
|
+
discoveredEntryPoints: []
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const discoveredEntryPoints = await this.discoverEntryPoints()
|
|
89
|
+
if (discoveredEntryPoints.length === 0) {
|
|
90
|
+
discoveredEntryPoints.push(this.config.ownPeerDescriptor)
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
discoveredEntryPoints,
|
|
94
|
+
entryPointsFromDht: true
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private async discoverEntryPoints(): Promise<PeerDescriptor[]> {
|
|
99
|
+
const dataKey = streamPartIdToDataKey(this.config.streamPartId)
|
|
100
|
+
const discoveredEntryPoints = await this.queryEntrypoints(dataKey)
|
|
101
|
+
const filtered = discoveredEntryPoints.filter((node) =>
|
|
102
|
+
!this.networkSplitAvoidedNodes.has(getNodeIdFromPeerDescriptor(node)))
|
|
103
|
+
// If all discovered entry points have previously been detected as offline, try again
|
|
104
|
+
if (filtered.length > 0) {
|
|
105
|
+
return filtered
|
|
106
|
+
} else {
|
|
107
|
+
return discoveredEntryPoints
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private async queryEntrypoints(key: Uint8Array): Promise<PeerDescriptor[]> {
|
|
112
|
+
logger.trace(`Finding data from dht node ${getNodeIdFromPeerDescriptor(this.config.ownPeerDescriptor)}`)
|
|
113
|
+
try {
|
|
114
|
+
const result = await this.config.getEntryPointData(key)
|
|
115
|
+
return parseEntryPointData(result)
|
|
116
|
+
} catch (err) {
|
|
117
|
+
return []
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async storeSelfAsEntryPointIfNecessary(currentEntrypointCount: number): Promise<void> {
|
|
122
|
+
if (this.abortController.signal.aborted) {
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
const possibleNetworkSplitDetected = this.config.layer1.getBucketSize() < NETWORK_SPLIT_AVOIDANCE_LIMIT
|
|
126
|
+
if ((currentEntrypointCount < ENTRYPOINT_STORE_LIMIT) || possibleNetworkSplitDetected) {
|
|
127
|
+
await this.storeSelfAsEntryPoint()
|
|
128
|
+
await this.keepSelfAsEntryPoint()
|
|
129
|
+
}
|
|
130
|
+
if (possibleNetworkSplitDetected) {
|
|
131
|
+
setImmediate(() => this.avoidNetworkSplit())
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private async storeSelfAsEntryPoint(): Promise<void> {
|
|
136
|
+
const ownPeerDescriptor = this.config.ownPeerDescriptor
|
|
137
|
+
const dataToStore = Any.pack(ownPeerDescriptor, PeerDescriptor)
|
|
138
|
+
try {
|
|
139
|
+
await this.config.storeEntryPointData(streamPartIdToDataKey(this.config.streamPartId), dataToStore)
|
|
140
|
+
} catch (err) {
|
|
141
|
+
logger.warn(`Failed to store self as entrypoint for ${this.config.streamPartId}`)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private async keepSelfAsEntryPoint(): Promise<void> {
|
|
146
|
+
await scheduleAtInterval(async () => {
|
|
147
|
+
logger.trace(`Attempting to keep self as entrypoint for ${this.config.streamPartId}`)
|
|
148
|
+
try {
|
|
149
|
+
const discovered = await this.discoverEntryPoints()
|
|
150
|
+
if (discovered.length < ENTRYPOINT_STORE_LIMIT
|
|
151
|
+
|| discovered.some((peerDescriptor) => areEqualPeerDescriptors(peerDescriptor, this.config.ownPeerDescriptor))) {
|
|
152
|
+
await this.storeSelfAsEntryPoint()
|
|
153
|
+
}
|
|
154
|
+
} catch (err) {
|
|
155
|
+
logger.debug(`Failed to keep self as entrypoint for ${this.config.streamPartId}`)
|
|
156
|
+
}
|
|
157
|
+
}, this.storeInterval, false, this.abortController.signal)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private async avoidNetworkSplit(): Promise<void> {
|
|
161
|
+
await exponentialRunOff(async () => {
|
|
162
|
+
const rediscoveredEntrypoints = await this.discoverEntryPoints()
|
|
163
|
+
await this.config.layer1.joinDht(rediscoveredEntrypoints, false, false)
|
|
164
|
+
if (this.config.layer1!.getBucketSize() < NETWORK_SPLIT_AVOIDANCE_LIMIT) {
|
|
165
|
+
// Filter out nodes that are not in the k-bucket, assumed to be offline
|
|
166
|
+
const nodesToAvoid = rediscoveredEntrypoints
|
|
167
|
+
.filter((peer) => !this.config.layer1!.getKBucketPeers().includes(peer))
|
|
168
|
+
.map((peer) => getNodeIdFromPeerDescriptor(peer))
|
|
169
|
+
nodesToAvoid.forEach((node) => this.networkSplitAvoidedNodes.add(node))
|
|
170
|
+
throw new Error(`Network split is still possible`)
|
|
171
|
+
}
|
|
172
|
+
}, 'avoid network split', this.abortController.signal)
|
|
173
|
+
this.networkSplitAvoidedNodes.clear()
|
|
174
|
+
logger.trace(`Network split avoided`)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async destroy(): Promise<void> {
|
|
178
|
+
this.abortController.abort()
|
|
179
|
+
await this.config.deleteEntryPointData(streamPartIdToDataKey(this.config.streamPartId))
|
|
180
|
+
}
|
|
181
|
+
}
|
package/src/logic/ILayer0.ts
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import { DataEntry, ITransport, PeerDescriptor
|
|
1
|
+
import { DataEntry, ITransport, PeerDescriptor } from '@streamr/dht'
|
|
2
2
|
import { Any } from '../proto/google/protobuf/any'
|
|
3
3
|
|
|
4
4
|
export interface ILayer0 extends ITransport {
|
|
5
5
|
getPeerDescriptor(): PeerDescriptor
|
|
6
|
-
|
|
7
|
-
getDataFromDht(key: Uint8Array): Promise<RecursiveFindResult>
|
|
8
|
-
findDataViaPeer(key: Uint8Array, node: PeerDescriptor): Promise<DataEntry[]>
|
|
6
|
+
getDataFromDht(key: Uint8Array): Promise<DataEntry[]>
|
|
9
7
|
storeDataToDht(key: Uint8Array, data: Any): Promise<PeerDescriptor[]>
|
|
10
8
|
deleteDataFromDht(key: Uint8Array): Promise<void>
|
|
11
|
-
|
|
12
|
-
isJoinOngoing(): boolean
|
|
9
|
+
waitForNetworkConnectivity(): Promise<void>
|
|
13
10
|
stop(): Promise<void>
|
|
14
11
|
}
|
package/src/logic/ILayer1.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PeerDescriptor
|
|
1
|
+
import { PeerDescriptor } from '@streamr/dht'
|
|
2
2
|
|
|
3
3
|
export interface ILayer1Events {
|
|
4
4
|
newContact: (peerDescriptor: PeerDescriptor, closestPeers: PeerDescriptor[]) => void
|
|
@@ -9,13 +9,10 @@ export interface ILayer1Events {
|
|
|
9
9
|
|
|
10
10
|
export interface ILayer1 {
|
|
11
11
|
on<T extends keyof ILayer1Events>(eventName: T, listener: (peerDescriptor: PeerDescriptor, peers: PeerDescriptor[]) => void): void
|
|
12
|
-
|
|
13
12
|
once<T extends keyof ILayer1Events>(eventName: T, listener: (peerDescriptor: PeerDescriptor, peers: PeerDescriptor[]) => void): void
|
|
14
|
-
|
|
15
13
|
off<T extends keyof ILayer1Events>(eventName: T, listener: (peerDescriptor: PeerDescriptor, peers: PeerDescriptor[]) => void): void
|
|
16
|
-
|
|
17
14
|
removeContact: (peerDescriptor: PeerDescriptor, removeFromOpenInternetPeers?: boolean) => void
|
|
18
|
-
|
|
15
|
+
getClosestContacts: (maxCount?: number) => PeerDescriptor[]
|
|
19
16
|
getKBucketPeers: () => PeerDescriptor[]
|
|
20
17
|
getBucketSize: () => number
|
|
21
18
|
joinDht: (entryPoints: PeerDescriptor[], doRandomJoin?: boolean, retry?: boolean) => Promise<void>
|
package/src/logic/NodeList.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { PeerDescriptor } from '@streamr/dht'
|
|
2
2
|
import { sample } from 'lodash'
|
|
3
|
-
import {
|
|
3
|
+
import { DeliveryRpcRemote } from './DeliveryRpcRemote'
|
|
4
4
|
import { EventEmitter } from 'eventemitter3'
|
|
5
5
|
import { getNodeIdFromPeerDescriptor, NodeID } from '../identifiers'
|
|
6
6
|
|
|
7
7
|
export interface Events {
|
|
8
|
-
nodeAdded: (id: NodeID, remote:
|
|
8
|
+
nodeAdded: (id: NodeID, remote: DeliveryRpcRemote) => any
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
const getValuesOfIncludedKeys = (nodes: Map<NodeID,
|
|
11
|
+
const getValuesOfIncludedKeys = (nodes: Map<NodeID, DeliveryRpcRemote>, exclude: NodeID[]): DeliveryRpcRemote[] => {
|
|
12
12
|
return Array.from(nodes.entries())
|
|
13
13
|
.filter(([id, _node]) => !exclude.includes(id))
|
|
14
14
|
.map(([_id, node]) => node)
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export class NodeList extends EventEmitter<Events> {
|
|
18
|
-
private readonly nodes: Map<NodeID,
|
|
18
|
+
private readonly nodes: Map<NodeID, DeliveryRpcRemote>
|
|
19
19
|
private readonly limit: number
|
|
20
20
|
private ownId: NodeID
|
|
21
21
|
|
|
@@ -26,7 +26,7 @@ export class NodeList extends EventEmitter<Events> {
|
|
|
26
26
|
this.ownId = ownId
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
add(remote:
|
|
29
|
+
add(remote: DeliveryRpcRemote): void {
|
|
30
30
|
const nodeId = getNodeIdFromPeerDescriptor(remote.getPeerDescriptor())
|
|
31
31
|
if ((this.ownId !== nodeId) && (this.nodes.size < this.limit)) {
|
|
32
32
|
const isExistingNode = this.nodes.has(nodeId)
|
|
@@ -54,7 +54,7 @@ export class NodeList extends EventEmitter<Events> {
|
|
|
54
54
|
return this.nodes.has(nodeId)
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
replaceAll(neighbors:
|
|
57
|
+
replaceAll(neighbors: DeliveryRpcRemote[]): void {
|
|
58
58
|
this.nodes.clear()
|
|
59
59
|
const limited = neighbors.splice(0, this.limit)
|
|
60
60
|
limited.forEach((remote) => {
|
|
@@ -66,7 +66,7 @@ export class NodeList extends EventEmitter<Events> {
|
|
|
66
66
|
return Array.from(this.nodes.keys())
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
get(id: NodeID): DeliveryRpcRemote | undefined {
|
|
70
70
|
return this.nodes.get(id)
|
|
71
71
|
}
|
|
72
72
|
|
|
@@ -74,16 +74,16 @@ export class NodeList extends EventEmitter<Events> {
|
|
|
74
74
|
return Array.from(this.nodes.keys()).filter((node) => !exclude.includes(node)).length
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
getRandom(exclude: NodeID[]):
|
|
77
|
+
getRandom(exclude: NodeID[]): DeliveryRpcRemote | undefined {
|
|
78
78
|
return sample(getValuesOfIncludedKeys(this.nodes, exclude))
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
getClosest(exclude: NodeID[]):
|
|
81
|
+
getClosest(exclude: NodeID[]): DeliveryRpcRemote | undefined {
|
|
82
82
|
const included = getValuesOfIncludedKeys(this.nodes, exclude)
|
|
83
83
|
return included[0]
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
getClosestAndFurthest(exclude: NodeID[]):
|
|
86
|
+
getClosestAndFurthest(exclude: NodeID[]): DeliveryRpcRemote[] {
|
|
87
87
|
const included = getValuesOfIncludedKeys(this.nodes, exclude)
|
|
88
88
|
if (included.length === 0) {
|
|
89
89
|
return []
|
|
@@ -91,12 +91,12 @@ export class NodeList extends EventEmitter<Events> {
|
|
|
91
91
|
return included.length > 1 ? [this.getClosest(exclude)!, this.getFurthest(exclude)!] : [this.getClosest(exclude)!]
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
getFurthest(exclude: NodeID[]):
|
|
94
|
+
getFurthest(exclude: NodeID[]): DeliveryRpcRemote | undefined {
|
|
95
95
|
const included = getValuesOfIncludedKeys(this.nodes, exclude)
|
|
96
96
|
return included[included.length - 1]
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
getAll(): DeliveryRpcRemote[] {
|
|
100
100
|
return Array.from(this.nodes.values())
|
|
101
101
|
}
|
|
102
102
|
|