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