@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.
- package/dist/package.json +5 -5
- package/package.json +5 -5
- package/src/NetworkNode.ts +0 -142
- package/src/NetworkStack.ts +0 -197
- package/src/exports.ts +0 -18
- package/src/logic/ContentDeliveryLayerNode.ts +0 -424
- package/src/logic/ContentDeliveryManager.ts +0 -401
- package/src/logic/ContentDeliveryRpcLocal.ts +0 -48
- package/src/logic/ContentDeliveryRpcRemote.ts +0 -44
- package/src/logic/ControlLayerNode.ts +0 -17
- package/src/logic/DiscoveryLayerNode.ts +0 -30
- package/src/logic/DuplicateMessageDetector.ts +0 -167
- package/src/logic/ExternalNetworkRpc.ts +0 -42
- package/src/logic/NodeList.ts +0 -114
- package/src/logic/PeerDescriptorStoreManager.ts +0 -96
- package/src/logic/StreamPartNetworkSplitAvoidance.ts +0 -90
- package/src/logic/StreamPartReconnect.ts +0 -38
- package/src/logic/createContentDeliveryLayerNode.ts +0 -130
- package/src/logic/formStreamPartDeliveryServiceId.ts +0 -7
- package/src/logic/inspect/InspectSession.ts +0 -55
- package/src/logic/inspect/Inspector.ts +0 -100
- package/src/logic/neighbor-discovery/HandshakeRpcLocal.ts +0 -138
- package/src/logic/neighbor-discovery/HandshakeRpcRemote.ts +0 -66
- package/src/logic/neighbor-discovery/Handshaker.ts +0 -215
- package/src/logic/neighbor-discovery/NeighborFinder.ts +0 -77
- package/src/logic/neighbor-discovery/NeighborUpdateManager.ts +0 -69
- package/src/logic/neighbor-discovery/NeighborUpdateRpcLocal.ts +0 -75
- package/src/logic/neighbor-discovery/NeighborUpdateRpcRemote.ts +0 -35
- package/src/logic/node-info/NodeInfoClient.ts +0 -23
- package/src/logic/node-info/NodeInfoRpcLocal.ts +0 -28
- package/src/logic/node-info/NodeInfoRpcRemote.ts +0 -11
- package/src/logic/propagation/FifoMapWithTTL.ts +0 -116
- package/src/logic/propagation/Propagation.ts +0 -84
- package/src/logic/propagation/PropagationTaskStore.ts +0 -41
- package/src/logic/proxy/ProxyClient.ts +0 -286
- package/src/logic/proxy/ProxyConnectionRpcLocal.ts +0 -106
- package/src/logic/proxy/ProxyConnectionRpcRemote.ts +0 -26
- package/src/logic/temporary-connection/TemporaryConnectionRpcLocal.ts +0 -73
- package/src/logic/temporary-connection/TemporaryConnectionRpcRemote.ts +0 -29
- package/src/logic/utils.ts +0 -18
- package/src/types.ts +0 -13
- package/test/benchmark/StreamPartIdDataKeyDistribution.test.ts +0 -60
- package/test/benchmark/first-message.ts +0 -171
- package/test/end-to-end/content-delivery-layer-node-with-real-connections.test.ts +0 -165
- package/test/end-to-end/external-network-rpc.test.ts +0 -67
- package/test/end-to-end/inspect.test.ts +0 -124
- package/test/end-to-end/proxy-and-full-node.test.ts +0 -143
- package/test/end-to-end/proxy-connections.test.ts +0 -226
- package/test/end-to-end/proxy-key-exchange.test.ts +0 -126
- package/test/end-to-end/webrtc-full-node-network.test.ts +0 -83
- package/test/end-to-end/websocket-full-node-network.test.ts +0 -82
- package/test/integration/ContentDeliveryLayerNode-Layer1Node-Latencies.test.ts +0 -139
- package/test/integration/ContentDeliveryLayerNode-Layer1Node.test.ts +0 -162
- package/test/integration/ContentDeliveryManager.test.ts +0 -160
- package/test/integration/ContentDeliveryRpcRemote.test.ts +0 -100
- package/test/integration/HandshakeRpcRemote.test.ts +0 -79
- package/test/integration/Handshakes.test.ts +0 -141
- package/test/integration/Inspect.test.ts +0 -89
- package/test/integration/NeighborUpdateRpcRemote.test.ts +0 -82
- package/test/integration/NetworkNode.test.ts +0 -115
- package/test/integration/NetworkRpc.test.ts +0 -52
- package/test/integration/NetworkStack.test.ts +0 -72
- package/test/integration/NodeInfoRpc.test.ts +0 -109
- package/test/integration/Propagation.test.ts +0 -76
- package/test/integration/joining-streams-on-offline-peers.test.ts +0 -82
- package/test/integration/stream-without-default-entrypoints.test.ts +0 -128
- package/test/integration/streamEntryPointReplacing.test.ts +0 -97
- package/test/types/global.d.ts +0 -1
- package/test/unit/ContentDeliveryLayerNode.test.ts +0 -112
- package/test/unit/ContentDeliveryManager.test.ts +0 -96
- package/test/unit/ContentDeliveryRpcLocal.test.ts +0 -60
- package/test/unit/DuplicateMessageDetector.test.ts +0 -192
- package/test/unit/ExternalNetworkRpc.test.ts +0 -48
- package/test/unit/FifoMapWithTtl.test.ts +0 -253
- package/test/unit/HandshakeRpcLocal.test.ts +0 -155
- package/test/unit/Handshaker.test.ts +0 -69
- package/test/unit/InspectSession.test.ts +0 -83
- package/test/unit/Inspector.test.ts +0 -51
- package/test/unit/NeighborFinder.test.ts +0 -51
- package/test/unit/NeighborUpdateRpcLocal.test.ts +0 -139
- package/test/unit/NetworkNode.test.ts +0 -42
- package/test/unit/NodeList.test.ts +0 -164
- package/test/unit/NumberPair.test.ts +0 -22
- package/test/unit/PeerDescriptorStoreManager.test.ts +0 -103
- package/test/unit/Propagation.test.ts +0 -151
- package/test/unit/ProxyConnectionRpcRemote.test.ts +0 -39
- package/test/unit/StreamPartIDDataKey.test.ts +0 -12
- package/test/unit/StreamPartNetworkSplitAvoidance.test.ts +0 -31
- package/test/unit/StreamPartReconnect.test.ts +0 -30
- package/test/unit/TemporaryConnectionRpcLocal.test.ts +0 -38
- package/test/utils/fake/FakePeerDescriptorStoreManager.ts +0 -29
- package/test/utils/mock/MockConnectionsView.ts +0 -18
- package/test/utils/mock/MockControlLayerNode.ts +0 -78
- package/test/utils/mock/MockDiscoveryLayerNode.ts +0 -60
- package/test/utils/mock/MockHandshaker.ts +0 -17
- package/test/utils/mock/MockNeighborFinder.ts +0 -20
- package/test/utils/mock/MockNeighborUpdateManager.ts +0 -21
- package/test/utils/mock/MockTransport.ts +0 -30
- package/test/utils/utils.ts +0 -144
- package/tsconfig.browser.json +0 -13
- package/tsconfig.jest.json +0 -17
- package/tsconfig.json +0 -3
- 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
|
-
}
|