@streamr/trackerless-network 102.0.0-beta.1 → 102.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/dist/package.json +5 -5
  2. package/package.json +5 -5
  3. package/src/NetworkNode.ts +0 -142
  4. package/src/NetworkStack.ts +0 -197
  5. package/src/exports.ts +0 -18
  6. package/src/logic/ContentDeliveryLayerNode.ts +0 -424
  7. package/src/logic/ContentDeliveryManager.ts +0 -401
  8. package/src/logic/ContentDeliveryRpcLocal.ts +0 -48
  9. package/src/logic/ContentDeliveryRpcRemote.ts +0 -44
  10. package/src/logic/ControlLayerNode.ts +0 -17
  11. package/src/logic/DiscoveryLayerNode.ts +0 -30
  12. package/src/logic/DuplicateMessageDetector.ts +0 -167
  13. package/src/logic/ExternalNetworkRpc.ts +0 -42
  14. package/src/logic/NodeList.ts +0 -114
  15. package/src/logic/PeerDescriptorStoreManager.ts +0 -96
  16. package/src/logic/StreamPartNetworkSplitAvoidance.ts +0 -90
  17. package/src/logic/StreamPartReconnect.ts +0 -38
  18. package/src/logic/createContentDeliveryLayerNode.ts +0 -130
  19. package/src/logic/formStreamPartDeliveryServiceId.ts +0 -7
  20. package/src/logic/inspect/InspectSession.ts +0 -55
  21. package/src/logic/inspect/Inspector.ts +0 -100
  22. package/src/logic/neighbor-discovery/HandshakeRpcLocal.ts +0 -138
  23. package/src/logic/neighbor-discovery/HandshakeRpcRemote.ts +0 -66
  24. package/src/logic/neighbor-discovery/Handshaker.ts +0 -215
  25. package/src/logic/neighbor-discovery/NeighborFinder.ts +0 -77
  26. package/src/logic/neighbor-discovery/NeighborUpdateManager.ts +0 -69
  27. package/src/logic/neighbor-discovery/NeighborUpdateRpcLocal.ts +0 -75
  28. package/src/logic/neighbor-discovery/NeighborUpdateRpcRemote.ts +0 -35
  29. package/src/logic/node-info/NodeInfoClient.ts +0 -23
  30. package/src/logic/node-info/NodeInfoRpcLocal.ts +0 -28
  31. package/src/logic/node-info/NodeInfoRpcRemote.ts +0 -11
  32. package/src/logic/propagation/FifoMapWithTTL.ts +0 -116
  33. package/src/logic/propagation/Propagation.ts +0 -84
  34. package/src/logic/propagation/PropagationTaskStore.ts +0 -41
  35. package/src/logic/proxy/ProxyClient.ts +0 -286
  36. package/src/logic/proxy/ProxyConnectionRpcLocal.ts +0 -106
  37. package/src/logic/proxy/ProxyConnectionRpcRemote.ts +0 -26
  38. package/src/logic/temporary-connection/TemporaryConnectionRpcLocal.ts +0 -73
  39. package/src/logic/temporary-connection/TemporaryConnectionRpcRemote.ts +0 -29
  40. package/src/logic/utils.ts +0 -18
  41. package/src/types.ts +0 -13
  42. package/test/benchmark/StreamPartIdDataKeyDistribution.test.ts +0 -60
  43. package/test/benchmark/first-message.ts +0 -171
  44. package/test/end-to-end/content-delivery-layer-node-with-real-connections.test.ts +0 -165
  45. package/test/end-to-end/external-network-rpc.test.ts +0 -67
  46. package/test/end-to-end/inspect.test.ts +0 -124
  47. package/test/end-to-end/proxy-and-full-node.test.ts +0 -143
  48. package/test/end-to-end/proxy-connections.test.ts +0 -226
  49. package/test/end-to-end/proxy-key-exchange.test.ts +0 -126
  50. package/test/end-to-end/webrtc-full-node-network.test.ts +0 -83
  51. package/test/end-to-end/websocket-full-node-network.test.ts +0 -82
  52. package/test/integration/ContentDeliveryLayerNode-Layer1Node-Latencies.test.ts +0 -139
  53. package/test/integration/ContentDeliveryLayerNode-Layer1Node.test.ts +0 -162
  54. package/test/integration/ContentDeliveryManager.test.ts +0 -160
  55. package/test/integration/ContentDeliveryRpcRemote.test.ts +0 -100
  56. package/test/integration/HandshakeRpcRemote.test.ts +0 -79
  57. package/test/integration/Handshakes.test.ts +0 -141
  58. package/test/integration/Inspect.test.ts +0 -89
  59. package/test/integration/NeighborUpdateRpcRemote.test.ts +0 -82
  60. package/test/integration/NetworkNode.test.ts +0 -115
  61. package/test/integration/NetworkRpc.test.ts +0 -52
  62. package/test/integration/NetworkStack.test.ts +0 -72
  63. package/test/integration/NodeInfoRpc.test.ts +0 -109
  64. package/test/integration/Propagation.test.ts +0 -76
  65. package/test/integration/joining-streams-on-offline-peers.test.ts +0 -82
  66. package/test/integration/stream-without-default-entrypoints.test.ts +0 -128
  67. package/test/integration/streamEntryPointReplacing.test.ts +0 -97
  68. package/test/types/global.d.ts +0 -1
  69. package/test/unit/ContentDeliveryLayerNode.test.ts +0 -112
  70. package/test/unit/ContentDeliveryManager.test.ts +0 -96
  71. package/test/unit/ContentDeliveryRpcLocal.test.ts +0 -60
  72. package/test/unit/DuplicateMessageDetector.test.ts +0 -192
  73. package/test/unit/ExternalNetworkRpc.test.ts +0 -48
  74. package/test/unit/FifoMapWithTtl.test.ts +0 -253
  75. package/test/unit/HandshakeRpcLocal.test.ts +0 -155
  76. package/test/unit/Handshaker.test.ts +0 -69
  77. package/test/unit/InspectSession.test.ts +0 -83
  78. package/test/unit/Inspector.test.ts +0 -51
  79. package/test/unit/NeighborFinder.test.ts +0 -51
  80. package/test/unit/NeighborUpdateRpcLocal.test.ts +0 -139
  81. package/test/unit/NetworkNode.test.ts +0 -42
  82. package/test/unit/NodeList.test.ts +0 -164
  83. package/test/unit/NumberPair.test.ts +0 -22
  84. package/test/unit/PeerDescriptorStoreManager.test.ts +0 -103
  85. package/test/unit/Propagation.test.ts +0 -151
  86. package/test/unit/ProxyConnectionRpcRemote.test.ts +0 -39
  87. package/test/unit/StreamPartIDDataKey.test.ts +0 -12
  88. package/test/unit/StreamPartNetworkSplitAvoidance.test.ts +0 -31
  89. package/test/unit/StreamPartReconnect.test.ts +0 -30
  90. package/test/unit/TemporaryConnectionRpcLocal.test.ts +0 -38
  91. package/test/utils/fake/FakePeerDescriptorStoreManager.ts +0 -29
  92. package/test/utils/mock/MockConnectionsView.ts +0 -18
  93. package/test/utils/mock/MockControlLayerNode.ts +0 -78
  94. package/test/utils/mock/MockDiscoveryLayerNode.ts +0 -60
  95. package/test/utils/mock/MockHandshaker.ts +0 -17
  96. package/test/utils/mock/MockNeighborFinder.ts +0 -20
  97. package/test/utils/mock/MockNeighborUpdateManager.ts +0 -21
  98. package/test/utils/mock/MockTransport.ts +0 -30
  99. package/test/utils/utils.ts +0 -144
  100. package/tsconfig.browser.json +0 -13
  101. package/tsconfig.jest.json +0 -17
  102. package/tsconfig.json +0 -3
  103. package/tsconfig.node.json +0 -17
@@ -1,55 +0,0 @@
1
- import { DhtAddress } from '@streamr/dht'
2
- import { toUserId } from '@streamr/utils'
3
- import { EventEmitter } from 'eventemitter3'
4
- import { MessageID } from '../../../generated/packages/trackerless-network/protos/NetworkRpc'
5
-
6
- export interface Events {
7
- done: () => void
8
- }
9
-
10
- interface InspectSessionOptions {
11
- inspectedNode: DhtAddress
12
- }
13
-
14
- const createMessageKey = (messageId: MessageID): string => {
15
- return `${toUserId(messageId.publisherId)}:${messageId.messageChainId}:${messageId.timestamp}:${messageId.sequenceNumber}`
16
- }
17
-
18
- export class InspectSession extends EventEmitter<Events> {
19
-
20
- // Boolean indicates if the message has been received by the inspected node
21
- private readonly inspectionMessages: Map<string, boolean> = new Map()
22
- private readonly inspectedNode: DhtAddress
23
-
24
- constructor(options: InspectSessionOptions) {
25
- super()
26
- this.inspectedNode = options.inspectedNode
27
- }
28
-
29
- markMessage(remoteNodeId: DhtAddress, messageId: MessageID): void {
30
- const messageKey = createMessageKey(messageId)
31
- if (!this.inspectionMessages.has(messageKey)) {
32
- this.inspectionMessages.set(messageKey, remoteNodeId === this.inspectedNode)
33
- } else if (this.inspectionMessages.has(messageKey)
34
- && this.inspectionMessages.get(messageKey) === false
35
- && remoteNodeId === this.inspectedNode
36
- ) {
37
- this.emit('done')
38
- } else if (this.inspectionMessages.has(messageKey)
39
- && this.inspectionMessages.get(messageKey) === true) {
40
- this.emit('done')
41
- }
42
- }
43
-
44
- getInspectedMessageCount(): number {
45
- return this.inspectionMessages.size
46
- }
47
-
48
- onlyMarkedByInspectedNode(): boolean {
49
- return Array.from(this.inspectionMessages.values()).every((value) => value === true)
50
- }
51
-
52
- stop(): void {
53
- this.emit('done')
54
- }
55
- }
@@ -1,100 +0,0 @@
1
- import { ConnectionLocker, DhtAddress, ListeningRpcCommunicator, LockID, PeerDescriptor, toNodeId } from '@streamr/dht'
2
- import { Logger, StreamPartID, waitForEvent3 } from '@streamr/utils'
3
- import { MessageID } from '../../../generated/packages/trackerless-network/protos/NetworkRpc'
4
- import { TemporaryConnectionRpcClient } from '../../../generated/packages/trackerless-network/protos/NetworkRpc.client'
5
- import { TemporaryConnectionRpcRemote } from '../temporary-connection/TemporaryConnectionRpcRemote'
6
- import { InspectSession, Events as InspectSessionEvents } from './InspectSession'
7
-
8
- interface InspectorOptions {
9
- localPeerDescriptor: PeerDescriptor
10
- streamPartId: StreamPartID
11
- rpcCommunicator: ListeningRpcCommunicator
12
- connectionLocker: ConnectionLocker
13
- inspectionTimeout?: number
14
- openInspectConnection?: (peerDescriptor: PeerDescriptor, lockId: LockID) => Promise<void>
15
- closeInspectConnection?: (peerDescriptor: PeerDescriptor, lockId: LockID) => Promise<void>
16
- }
17
-
18
- const logger = new Logger(module)
19
- const DEFAULT_TIMEOUT = 60 * 1000
20
-
21
- export class Inspector {
22
-
23
- private readonly sessions: Map<DhtAddress, InspectSession> = new Map()
24
- private readonly streamPartId: StreamPartID
25
- private readonly localPeerDescriptor: PeerDescriptor
26
- private readonly rpcCommunicator: ListeningRpcCommunicator
27
- private readonly connectionLocker: ConnectionLocker
28
- private readonly inspectionTimeout: number
29
- private readonly openInspectConnection: (peerDescriptor: PeerDescriptor, lockId: LockID) => Promise<void>
30
- private readonly closeInspectConnection: (peerDescriptor: PeerDescriptor, lockId: LockID) => Promise<void>
31
-
32
- constructor(options: InspectorOptions) {
33
- this.streamPartId = options.streamPartId
34
- this.localPeerDescriptor = options.localPeerDescriptor
35
- this.rpcCommunicator = options.rpcCommunicator
36
- this.connectionLocker = options.connectionLocker
37
- this.inspectionTimeout = options.inspectionTimeout ?? DEFAULT_TIMEOUT
38
- this.openInspectConnection = options.openInspectConnection ?? this.defaultOpenInspectConnection
39
- this.closeInspectConnection = options.closeInspectConnection ?? this.defaultCloseInspectConnection
40
- }
41
-
42
- async defaultOpenInspectConnection(peerDescriptor: PeerDescriptor, lockId: LockID): Promise<void> {
43
- const rpcRemote = new TemporaryConnectionRpcRemote(
44
- this.localPeerDescriptor,
45
- peerDescriptor,
46
- this.rpcCommunicator,
47
- TemporaryConnectionRpcClient
48
- )
49
- await rpcRemote.openConnection()
50
- this.connectionLocker.weakLockConnection(toNodeId(peerDescriptor), lockId)
51
- }
52
-
53
- async defaultCloseInspectConnection(peerDescriptor: PeerDescriptor, lockId: LockID): Promise<void> {
54
- const rpcRemote = new TemporaryConnectionRpcRemote(
55
- this.localPeerDescriptor,
56
- peerDescriptor,
57
- this.rpcCommunicator,
58
- TemporaryConnectionRpcClient
59
- )
60
- await rpcRemote.closeConnection()
61
- this.connectionLocker.weakUnlockConnection(toNodeId(peerDescriptor), lockId)
62
- }
63
-
64
- async inspect(peerDescriptor: PeerDescriptor): Promise<boolean> {
65
- const nodeId = toNodeId(peerDescriptor)
66
- const session = new InspectSession({
67
- inspectedNode: nodeId
68
- })
69
- const lockId = `inspector-${this.streamPartId}`
70
- this.sessions.set(nodeId, session)
71
- await this.openInspectConnection(peerDescriptor, lockId)
72
- let success = false
73
- try {
74
- await waitForEvent3<InspectSessionEvents>(session, 'done', this.inspectionTimeout)
75
- success = true
76
- } catch {
77
- logger.trace('Inspect session timed out, removing')
78
- } finally {
79
- await this.closeInspectConnection(peerDescriptor, lockId)
80
- this.sessions.delete(nodeId)
81
- }
82
- return success || session.getInspectedMessageCount() < 1 || session.onlyMarkedByInspectedNode()
83
- }
84
-
85
- markMessage(sender: DhtAddress, messageId: MessageID): void {
86
- this.sessions.forEach((session) => session.markMessage(sender, messageId))
87
- }
88
-
89
- isInspected(nodeId: DhtAddress): boolean {
90
- return this.sessions.has(nodeId)
91
- }
92
-
93
- stop(): void {
94
- this.sessions.forEach((session) => {
95
- session.stop()
96
- })
97
- this.sessions.clear()
98
- }
99
-
100
- }
@@ -1,138 +0,0 @@
1
- import { ServerCallContext } from '@protobuf-ts/runtime-rpc'
2
- import {
3
- DhtAddress,
4
- DhtAddressRaw,
5
- DhtCallContext,
6
- PeerDescriptor,
7
- toDhtAddress,
8
- toNodeId
9
- } from '@streamr/dht'
10
- import { Logger, StreamPartID } from '@streamr/utils'
11
- import {
12
- InterleaveRequest,
13
- InterleaveResponse,
14
- StreamPartHandshakeRequest,
15
- StreamPartHandshakeResponse
16
- } from '../../../generated/packages/trackerless-network/protos/NetworkRpc'
17
- import { IHandshakeRpc } from '../../../generated/packages/trackerless-network/protos/NetworkRpc.server'
18
- import { ContentDeliveryRpcRemote } from '../ContentDeliveryRpcRemote'
19
- import { NodeList } from '../NodeList'
20
- import { HandshakeRpcRemote } from './HandshakeRpcRemote'
21
-
22
- interface HandshakeRpcLocalOptions {
23
- streamPartId: StreamPartID
24
- neighbors: NodeList
25
- ongoingHandshakes: Set<DhtAddress>
26
- ongoingInterleaves: Set<DhtAddress>
27
- maxNeighborCount: number
28
- createRpcRemote: (target: PeerDescriptor) => HandshakeRpcRemote
29
- createContentDeliveryRpcRemote: (peerDescriptor: PeerDescriptor) => ContentDeliveryRpcRemote
30
- handshakeWithInterleaving: (target: PeerDescriptor, remoteNodeId: DhtAddress) => Promise<boolean>
31
- }
32
-
33
- const logger = new Logger(module)
34
-
35
- export class HandshakeRpcLocal implements IHandshakeRpc {
36
-
37
- private readonly options: HandshakeRpcLocalOptions
38
-
39
- constructor(options: HandshakeRpcLocalOptions) {
40
- this.options = options
41
- }
42
-
43
- async handshake(request: StreamPartHandshakeRequest, context: ServerCallContext): Promise<StreamPartHandshakeResponse> {
44
- return this.handleRequest(request, context)
45
- }
46
-
47
- private handleRequest(request: StreamPartHandshakeRequest, context: ServerCallContext): StreamPartHandshakeResponse {
48
- const senderDescriptor = (context as DhtCallContext).incomingSourceDescriptor!
49
- const getInterleaveNodeIds = () => (request.interleaveNodeId !== undefined) ? [toDhtAddress(request.interleaveNodeId)] : []
50
- const senderNodeId = toNodeId(senderDescriptor)
51
- if (this.options.ongoingInterleaves.has(senderNodeId)) {
52
- return this.rejectHandshake(request)
53
- } else if (this.options.neighbors.has(senderNodeId)
54
- || this.options.ongoingHandshakes.has(senderNodeId)
55
- ) {
56
- return this.acceptHandshake(request, senderDescriptor)
57
- } else if (this.options.neighbors.size() + this.options.ongoingHandshakes.size < this.options.maxNeighborCount) {
58
- return this.acceptHandshake(request, senderDescriptor)
59
- } else if (
60
- this.options.neighbors.size(getInterleaveNodeIds()) - this.options.ongoingInterleaves.size >= 2
61
- && this.options.neighbors.size() <= this.options.maxNeighborCount
62
- ) {
63
- // Do not accept the handshakes requests if the target neighbor count can potentially drop below 2
64
- // due to interleaving. This ensures that a stable number of connections is kept during high churn.
65
- return this.acceptHandshakeWithInterleaving(request, senderDescriptor)
66
- } else {
67
- return this.rejectHandshake(request)
68
- }
69
- }
70
-
71
- private acceptHandshake(request: StreamPartHandshakeRequest, requester: PeerDescriptor) {
72
- const res: StreamPartHandshakeResponse = {
73
- requestId: request.requestId,
74
- accepted: true
75
- }
76
- this.options.neighbors.add(this.options.createContentDeliveryRpcRemote(requester))
77
- return res
78
- }
79
-
80
- // eslint-disable-next-line class-methods-use-this
81
- private rejectHandshake(request: StreamPartHandshakeRequest) {
82
- const res: StreamPartHandshakeResponse = {
83
- requestId: request.requestId,
84
- accepted: false
85
- }
86
- return res
87
- }
88
-
89
- private acceptHandshakeWithInterleaving(request: StreamPartHandshakeRequest, requester: PeerDescriptor): StreamPartHandshakeResponse {
90
- const exclude: DhtAddress[] = []
91
- request.neighborNodeIds.forEach((id: DhtAddressRaw) => exclude.push(toDhtAddress(id)))
92
- this.options.ongoingInterleaves.forEach((id) => exclude.push(id))
93
- exclude.push(toNodeId(requester))
94
- if (request.interleaveNodeId !== undefined) {
95
- exclude.push(toDhtAddress(request.interleaveNodeId))
96
- }
97
- const last = this.options.neighbors.getLast(exclude)
98
- const lastPeerDescriptor = last ? last.getPeerDescriptor() : undefined
99
- if (last) {
100
- const nodeId = toNodeId(last.getPeerDescriptor())
101
- const remote = this.options.createRpcRemote(last.getPeerDescriptor())
102
- this.options.ongoingInterleaves.add(nodeId)
103
- // Run this with then catch instead of setImmediate to avoid changes in state
104
- // eslint-disable-next-line promise/catch-or-return
105
- remote.interleaveRequest(requester).then((response) => {
106
- // If response is accepted, remove the last node from the target neighbors
107
- // and unlock the connection
108
- // If response is not accepted, keep the last node as a neighbor
109
- if (response.accepted) {
110
- this.options.neighbors.remove(toNodeId(lastPeerDescriptor!))
111
- }
112
- }).catch(() => {
113
- // no-op: InterleaveRequest cannot reject
114
- }).finally(() => {
115
- this.options.ongoingInterleaves.delete(nodeId)
116
- })
117
- }
118
- this.options.neighbors.add(this.options.createContentDeliveryRpcRemote(requester))
119
- return {
120
- requestId: request.requestId,
121
- accepted: true,
122
- interleaveTargetDescriptor: lastPeerDescriptor
123
- }
124
- }
125
-
126
- async interleaveRequest(message: InterleaveRequest, context: ServerCallContext): Promise<InterleaveResponse> {
127
- const senderPeerDescriptor = (context as DhtCallContext).incomingSourceDescriptor!
128
- const remoteNodeId = toNodeId(senderPeerDescriptor)
129
- try {
130
- await this.options.handshakeWithInterleaving(message.interleaveTargetDescriptor!, remoteNodeId)
131
- this.options.neighbors.remove(remoteNodeId)
132
- return { accepted: true }
133
- } catch (err) {
134
- logger.debug(`interleaveRequest to ${toNodeId(message.interleaveTargetDescriptor!)} failed`, { err })
135
- return { accepted: false }
136
- }
137
- }
138
- }
@@ -1,66 +0,0 @@
1
- import { DhtAddress, PeerDescriptor, RpcRemote, toNodeId, toDhtAddressRaw } from '@streamr/dht'
2
- import { Logger, StreamPartID } from '@streamr/utils'
3
- import { v4 } from 'uuid'
4
- import { InterleaveRequest, InterleaveResponse, StreamPartHandshakeRequest } from '../../../generated/packages/trackerless-network/protos/NetworkRpc'
5
- import { HandshakeRpcClient } from '../../../generated/packages/trackerless-network/protos/NetworkRpc.client'
6
-
7
- const logger = new Logger(module)
8
-
9
- interface HandshakeResponse {
10
- accepted: boolean
11
- interleaveTargetDescriptor?: PeerDescriptor
12
- }
13
-
14
- export const INTERLEAVE_REQUEST_TIMEOUT = 10000
15
-
16
- export class HandshakeRpcRemote extends RpcRemote<HandshakeRpcClient> {
17
-
18
- async handshake(
19
- streamPartId: StreamPartID,
20
- neighborNodeIds: DhtAddress[],
21
- concurrentHandshakeNodeId?: DhtAddress,
22
- interleaveNodeId?: DhtAddress
23
- ): Promise<HandshakeResponse> {
24
- const request: StreamPartHandshakeRequest = {
25
- streamPartId,
26
- requestId: v4(),
27
- neighborNodeIds: neighborNodeIds.map((id) => toDhtAddressRaw(id)),
28
- concurrentHandshakeNodeId: (concurrentHandshakeNodeId !== undefined) ? toDhtAddressRaw(concurrentHandshakeNodeId) : undefined,
29
- interleaveNodeId: (interleaveNodeId !== undefined) ? toDhtAddressRaw(interleaveNodeId) : undefined
30
- }
31
- try {
32
- const response = await this.getClient().handshake(request, this.formDhtRpcOptions())
33
- return {
34
- accepted: response.accepted,
35
- interleaveTargetDescriptor: response.interleaveTargetDescriptor
36
- }
37
- } catch (err: any) {
38
- logger.debug(`handshake to ${toNodeId(this.getPeerDescriptor())} failed`, { err })
39
- return {
40
- accepted: false
41
- }
42
- }
43
- }
44
-
45
- async interleaveRequest(originatorDescriptor: PeerDescriptor): Promise<InterleaveResponse> {
46
- const request: InterleaveRequest = {
47
- interleaveTargetDescriptor: originatorDescriptor
48
- }
49
- const options = this.formDhtRpcOptions({
50
- connect: false,
51
- timeout: INTERLEAVE_REQUEST_TIMEOUT
52
- })
53
- try {
54
- const res = await this.getClient().interleaveRequest(request, options)
55
- return {
56
- accepted: res.accepted
57
- }
58
- } catch (err) {
59
- logger.debug(`interleaveRequest to ${toNodeId(this.getPeerDescriptor())} failed`, { err })
60
- return {
61
- accepted: false
62
- }
63
- }
64
-
65
- }
66
- }
@@ -1,215 +0,0 @@
1
- import { DhtAddress, ListeningRpcCommunicator, PeerDescriptor, toNodeId } from '@streamr/dht'
2
- import { Logger, StreamPartID } from '@streamr/utils'
3
- import {
4
- InterleaveRequest,
5
- InterleaveResponse,
6
- StreamPartHandshakeRequest,
7
- StreamPartHandshakeResponse
8
- } from '../../../generated/packages/trackerless-network/protos/NetworkRpc'
9
- import {
10
- ContentDeliveryRpcClient, HandshakeRpcClient
11
- } from '../../../generated/packages/trackerless-network/protos/NetworkRpc.client'
12
- import { ContentDeliveryRpcRemote } from '../ContentDeliveryRpcRemote'
13
- import { NodeList } from '../NodeList'
14
- import { HandshakeRpcLocal } from './HandshakeRpcLocal'
15
- import { HandshakeRpcRemote, INTERLEAVE_REQUEST_TIMEOUT } from './HandshakeRpcRemote'
16
-
17
- interface HandshakerOptions {
18
- localPeerDescriptor: PeerDescriptor
19
- streamPartId: StreamPartID
20
- neighbors: NodeList
21
- leftNodeView: NodeList
22
- rightNodeView: NodeList
23
- nearbyNodeView: NodeList
24
- randomNodeView: NodeList
25
- rpcCommunicator: ListeningRpcCommunicator
26
- maxNeighborCount: number
27
- ongoingHandshakes: Set<DhtAddress>
28
- rpcRequestTimeout?: number
29
- }
30
-
31
- const logger = new Logger(module)
32
-
33
- const PARALLEL_HANDSHAKE_COUNT = 2
34
-
35
- export class Handshaker {
36
-
37
- private options: HandshakerOptions
38
- private readonly rpcLocal: HandshakeRpcLocal
39
-
40
- constructor(options: HandshakerOptions) {
41
- this.options = options
42
- this.rpcLocal = new HandshakeRpcLocal({
43
- streamPartId: this.options.streamPartId,
44
- neighbors: this.options.neighbors,
45
- ongoingHandshakes: this.options.ongoingHandshakes,
46
- ongoingInterleaves: new Set(),
47
- maxNeighborCount: this.options.maxNeighborCount,
48
- handshakeWithInterleaving: (target: PeerDescriptor, remoteNodeId: DhtAddress) => this.handshakeWithInterleaving(target, remoteNodeId),
49
- createRpcRemote: (target: PeerDescriptor) => this.createRpcRemote(target),
50
- createContentDeliveryRpcRemote: (target: PeerDescriptor) => this.createContentDeliveryRpcRemote(target)
51
- })
52
- this.options.rpcCommunicator.registerRpcMethod(InterleaveRequest, InterleaveResponse, 'interleaveRequest',
53
- (req: InterleaveRequest, context) => this.rpcLocal.interleaveRequest(req, context), { timeout: INTERLEAVE_REQUEST_TIMEOUT })
54
- this.options.rpcCommunicator.registerRpcMethod(StreamPartHandshakeRequest, StreamPartHandshakeResponse, 'handshake',
55
- (req: StreamPartHandshakeRequest, context) => this.rpcLocal.handshake(req, context))
56
- }
57
-
58
- async attemptHandshakesOnContacts(excludedIds: DhtAddress[]): Promise<DhtAddress[]> {
59
- // TODO use options option or named constant? or why the value 2?
60
- if (this.options.neighbors.size() + this.options.ongoingHandshakes.size < this.options.maxNeighborCount - 2) {
61
- logger.trace(`Attempting parallel handshakes with ${PARALLEL_HANDSHAKE_COUNT} targets`)
62
- return this.selectParallelTargetsAndHandshake(excludedIds)
63
- } else if (this.options.neighbors.size() + this.options.ongoingHandshakes.size < this.options.maxNeighborCount) {
64
- logger.trace(`Attempting handshake with new target`)
65
- return this.selectNewTargetAndHandshake(excludedIds)
66
- }
67
- return excludedIds
68
- }
69
-
70
- private async selectParallelTargetsAndHandshake(excludedIds: DhtAddress[]): Promise<DhtAddress[]> {
71
- const exclude = excludedIds.concat(this.options.neighbors.getIds())
72
- const neighbors = this.selectParallelTargets(exclude)
73
- neighbors.forEach((contact) => this.options.ongoingHandshakes.add(toNodeId(contact.getPeerDescriptor())))
74
- return this.doParallelHandshakes(neighbors, exclude)
75
- }
76
-
77
- private selectParallelTargets(excludedIds: DhtAddress[]): HandshakeRpcRemote[] {
78
- const neighbors: Map<DhtAddress, ContentDeliveryRpcRemote> = new Map()
79
- // If the node has 0 neighbors find a node in the stream with a WS server to connect to for faster time to data.
80
- if (this.options.neighbors.size() === 0) {
81
- const wsNode = this.options.nearbyNodeView.getFirst(
82
- [...excludedIds, ...Array.from(neighbors.keys())] as DhtAddress[],
83
- true
84
- )
85
- if (wsNode) {
86
- const wsNodeId = toNodeId(wsNode.getPeerDescriptor())
87
- excludedIds.push(wsNodeId)
88
- neighbors.set(wsNodeId, wsNode)
89
- }
90
- }
91
- // Add the closest left and then right contacts from the ring if possible.
92
- const left = this.options.leftNodeView.getFirst([...excludedIds, ...Array.from(neighbors.keys())] as DhtAddress[])
93
- const right = this.options.rightNodeView.getFirst([...excludedIds, ...Array.from(neighbors.keys())] as DhtAddress[])
94
- if (left) {
95
- neighbors.set(toNodeId(left.getPeerDescriptor()), left)
96
- }
97
- if (right) {
98
- neighbors.set(toNodeId(right.getPeerDescriptor()), right)
99
- }
100
- // If there is still room add the closest contact based on the kademlia metric
101
- if (neighbors.size < PARALLEL_HANDSHAKE_COUNT) {
102
- const first = this.options.nearbyNodeView.getFirst([...excludedIds, ...Array.from(neighbors.keys())] as DhtAddress[])
103
- if (first) {
104
- neighbors.set(toNodeId(first.getPeerDescriptor()), first)
105
- }
106
- }
107
- const getExcludedFromRandomView = () => [
108
- ...excludedIds,
109
- ...Array.from(neighbors.values()).map((neighbor) => toNodeId(neighbor.getPeerDescriptor()))
110
- ]
111
- // If there is still room add a random contact until PARALLEL_HANDSHAKE_COUNT is reached
112
- while (
113
- neighbors.size < PARALLEL_HANDSHAKE_COUNT
114
- && this.options.randomNodeView.size(getExcludedFromRandomView()) > 0
115
- ) {
116
- const random = this.options.randomNodeView.getRandom([...excludedIds, ...Array.from(neighbors.keys())] as DhtAddress[])
117
- if (random) {
118
- neighbors.set(toNodeId(random.getPeerDescriptor()), random)
119
- }
120
- }
121
- return Array.from(neighbors.values()).map((neighbor) => this.createRpcRemote(neighbor.getPeerDescriptor()))
122
- }
123
-
124
- private async doParallelHandshakes(targets: HandshakeRpcRemote[], excludedIds: DhtAddress[]): Promise<DhtAddress[]> {
125
- const results = await Promise.allSettled(
126
- Array.from(targets.values()).map(async (target: HandshakeRpcRemote, i) => {
127
- const otherNode = i === 0 ? targets[1] : targets[0]
128
- // TODO better check (currently this condition is always true)
129
- const otherNodeId = otherNode ? toNodeId(otherNode.getPeerDescriptor()) : undefined
130
- return this.handshakeWithTarget(target, otherNodeId)
131
- })
132
- )
133
- results.forEach((res, i) => {
134
- if (res.status !== 'fulfilled' || !res.value) {
135
- excludedIds.push(toNodeId(targets[i].getPeerDescriptor()))
136
- }
137
- })
138
- return excludedIds
139
- }
140
-
141
- private async selectNewTargetAndHandshake(excludedIds: DhtAddress[]): Promise<DhtAddress[]> {
142
- const exclude = excludedIds.concat(this.options.neighbors.getIds())
143
- const neighbor = this.options.leftNodeView.getFirst(exclude)
144
- ?? this.options.rightNodeView.getFirst(exclude)
145
- ?? this.options.nearbyNodeView.getFirst(exclude)
146
- ?? this.options.randomNodeView.getRandom(exclude)
147
- if (neighbor) {
148
- const accepted = await this.handshakeWithTarget(this.createRpcRemote(neighbor.getPeerDescriptor()))
149
- if (!accepted) {
150
- excludedIds.push(toNodeId(neighbor.getPeerDescriptor()))
151
- }
152
- }
153
- return excludedIds
154
- }
155
-
156
- private async handshakeWithTarget(neighbor: HandshakeRpcRemote, concurrentNodeId?: DhtAddress): Promise<boolean> {
157
- const targetNodeId = toNodeId(neighbor.getPeerDescriptor())
158
- this.options.ongoingHandshakes.add(targetNodeId)
159
- const result = await neighbor.handshake(
160
- this.options.streamPartId,
161
- this.options.neighbors.getIds(),
162
- concurrentNodeId
163
- )
164
- if (result.accepted) {
165
- this.options.neighbors.add(this.createContentDeliveryRpcRemote(neighbor.getPeerDescriptor()))
166
- }
167
- if (result.interleaveTargetDescriptor) {
168
- await this.handshakeWithInterleaving(result.interleaveTargetDescriptor, targetNodeId)
169
- }
170
- this.options.ongoingHandshakes.delete(targetNodeId)
171
- return result.accepted
172
- }
173
-
174
- private async handshakeWithInterleaving(target: PeerDescriptor, remoteNodeId: DhtAddress): Promise<boolean> {
175
- const neighbor = this.createRpcRemote(target)
176
- const targetNodeId = toNodeId(neighbor.getPeerDescriptor())
177
- this.options.ongoingHandshakes.add(targetNodeId)
178
- const result = await neighbor.handshake(
179
- this.options.streamPartId,
180
- this.options.neighbors.getIds(),
181
- undefined,
182
- remoteNodeId
183
- )
184
- if (result.accepted) {
185
- this.options.neighbors.add(this.createContentDeliveryRpcRemote(neighbor.getPeerDescriptor()))
186
- }
187
- this.options.ongoingHandshakes.delete(targetNodeId)
188
- return result.accepted
189
- }
190
-
191
- private createRpcRemote(targetPeerDescriptor: PeerDescriptor): HandshakeRpcRemote {
192
- return new HandshakeRpcRemote(
193
- this.options.localPeerDescriptor,
194
- targetPeerDescriptor,
195
- this.options.rpcCommunicator,
196
- HandshakeRpcClient,
197
- this.options.rpcRequestTimeout
198
- )
199
- }
200
-
201
- private createContentDeliveryRpcRemote(targetPeerDescriptor: PeerDescriptor): ContentDeliveryRpcRemote {
202
- return new ContentDeliveryRpcRemote(
203
- this.options.localPeerDescriptor,
204
- targetPeerDescriptor,
205
- this.options.rpcCommunicator,
206
- ContentDeliveryRpcClient,
207
- this.options.rpcRequestTimeout
208
- )
209
- }
210
-
211
- getOngoingHandshakes(): Set<DhtAddress> {
212
- return this.options.ongoingHandshakes
213
- }
214
-
215
- }
@@ -1,77 +0,0 @@
1
- import { Logger, setAbortableTimeout } from '@streamr/utils'
2
- import { NodeList } from '../NodeList'
3
- import { DhtAddress } from '@streamr/dht'
4
-
5
- interface FindNeighborsSessionOptions {
6
- neighbors: NodeList
7
- nearbyNodeView: NodeList
8
- leftNodeView: NodeList
9
- rightNodeView: NodeList
10
- randomNodeView: NodeList
11
- doFindNeighbors: (excludedNodes: DhtAddress[]) => Promise<DhtAddress[]>
12
- minCount: number
13
- }
14
-
15
- const INITIAL_WAIT = 100
16
- const INTERVAL = 250
17
-
18
- const logger = new Logger(module)
19
-
20
- export class NeighborFinder {
21
- private readonly abortController: AbortController
22
- private readonly options: FindNeighborsSessionOptions
23
- private running = false
24
-
25
- constructor(options: FindNeighborsSessionOptions) {
26
- this.options = options
27
- this.abortController = new AbortController()
28
- }
29
-
30
- private async findNeighbors(excluded: DhtAddress[]): Promise<void> {
31
- if (!this.running) {
32
- return
33
- }
34
- const newExcludes = await this.options.doFindNeighbors(excluded)
35
- const uniqueContactCount = new Set([
36
- ...this.options.nearbyNodeView.getIds(),
37
- ...this.options.leftNodeView.getIds(),
38
- ...this.options.rightNodeView.getIds(),
39
- ...this.options.randomNodeView.getIds()
40
- ]).size
41
- if (this.options.neighbors.size() < this.options.minCount && newExcludes.length < uniqueContactCount) {
42
- // TODO should we catch possible promise rejection?
43
- setAbortableTimeout(() => this.findNeighbors(newExcludes), INTERVAL, this.abortController.signal)
44
- } else if (this.options.neighbors.size() === 0 && uniqueContactCount > 0) {
45
- logger.debug('No neighbors found yet contacts are available, restarting handshaking process')
46
- setAbortableTimeout(() => this.findNeighbors([]), INTERVAL, this.abortController.signal)
47
- } else {
48
- this.running = false
49
- }
50
- }
51
-
52
- isRunning(): boolean {
53
- return this.running
54
- }
55
-
56
- start(excluded: DhtAddress[] = []): void {
57
- if (this.running) {
58
- return
59
- }
60
- this.running = true
61
- // TODO should we catch possible promise rejection?
62
- setAbortableTimeout(async () => {
63
- await Promise.all([
64
- this.findNeighbors(excluded),
65
- this.findNeighbors(excluded)
66
- ])
67
- }, INITIAL_WAIT, this.abortController.signal)
68
- }
69
-
70
- stop(): void {
71
- if (!this.running) {
72
- return
73
- }
74
- this.running = false
75
- this.abortController.abort()
76
- }
77
- }