@streamr/dht 100.0.0-testnet-one.0 → 100.0.0-testnet-one.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/src/connection/ConnectionLockRpcRemote.js +1 -25
- package/dist/src/connection/ConnectionLockRpcRemote.js.map +1 -1
- package/dist/src/connection/ConnectionManager.d.ts +0 -1
- package/dist/src/connection/ConnectionManager.js +7 -6
- package/dist/src/connection/ConnectionManager.js.map +1 -1
- package/dist/src/connection/ConnectorFacade.d.ts +2 -2
- package/dist/src/connection/ConnectorFacade.js +2 -3
- package/dist/src/connection/ConnectorFacade.js.map +1 -1
- package/dist/src/connection/ManagedConnection.d.ts +1 -0
- package/dist/src/connection/ManagedConnection.js +11 -1
- package/dist/src/connection/ManagedConnection.js.map +1 -1
- package/dist/src/connection/connectivityChecker.js +3 -2
- package/dist/src/connection/connectivityChecker.js.map +1 -1
- package/dist/src/connection/websocket/ClientWebsocket.d.ts +1 -0
- package/dist/src/connection/websocket/ClientWebsocket.js +6 -3
- package/dist/src/connection/websocket/ClientWebsocket.js.map +1 -1
- package/dist/src/connection/websocket/ServerWebsocket.d.ts +4 -0
- package/dist/src/connection/websocket/ServerWebsocket.js +32 -21
- package/dist/src/connection/websocket/ServerWebsocket.js.map +1 -1
- package/dist/src/connection/websocket/WebsocketConnector.d.ts +0 -1
- package/dist/src/connection/websocket/WebsocketConnector.js +21 -10
- package/dist/src/connection/websocket/WebsocketConnector.js.map +1 -1
- package/dist/src/connection/websocket/WebsocketConnectorRpcLocal.d.ts +1 -1
- package/dist/src/connection/websocket/WebsocketConnectorRpcLocal.js +8 -11
- package/dist/src/connection/websocket/WebsocketConnectorRpcLocal.js.map +1 -1
- package/dist/src/connection/websocket/WebsocketConnectorRpcRemote.d.ts +2 -2
- package/dist/src/connection/websocket/WebsocketConnectorRpcRemote.js +3 -37
- package/dist/src/connection/websocket/WebsocketConnectorRpcRemote.js.map +1 -1
- package/dist/src/connection/websocket/WebsocketServer.js +21 -4
- package/dist/src/connection/websocket/WebsocketServer.js.map +1 -1
- package/dist/src/dht/DhtNode.d.ts +4 -4
- package/dist/src/dht/DhtNode.js +31 -20
- package/dist/src/dht/DhtNode.js.map +1 -1
- package/dist/src/dht/DhtNodeRpcLocal.d.ts +1 -4
- package/dist/src/dht/DhtNodeRpcLocal.js +1 -5
- package/dist/src/dht/DhtNodeRpcLocal.js.map +1 -1
- package/dist/src/dht/PeerManager.d.ts +10 -6
- package/dist/src/dht/PeerManager.js +95 -30
- package/dist/src/dht/PeerManager.js.map +1 -1
- package/dist/src/dht/contact/SortedContactList.d.ts +20 -6
- package/dist/src/dht/contact/SortedContactList.js +55 -24
- package/dist/src/dht/contact/SortedContactList.js.map +1 -1
- package/dist/src/dht/discovery/DiscoverySession.d.ts +3 -5
- package/dist/src/dht/discovery/DiscoverySession.js +11 -9
- package/dist/src/dht/discovery/DiscoverySession.js.map +1 -1
- package/dist/src/dht/discovery/PeerDiscovery.d.ts +4 -2
- package/dist/src/dht/discovery/PeerDiscovery.js +17 -16
- package/dist/src/dht/discovery/PeerDiscovery.js.map +1 -1
- package/dist/src/dht/find/FindSession.js +6 -1
- package/dist/src/dht/find/FindSession.js.map +1 -1
- package/dist/src/dht/find/Finder.js +6 -1
- package/dist/src/dht/find/Finder.js.map +1 -1
- package/dist/src/dht/routing/Router.d.ts +1 -1
- package/dist/src/dht/routing/Router.js +8 -4
- package/dist/src/dht/routing/Router.js.map +1 -1
- package/dist/src/dht/routing/RoutingSession.js +8 -1
- package/dist/src/dht/routing/RoutingSession.js.map +1 -1
- package/dist/src/dht/store/StoreRpcLocal.js +19 -5
- package/dist/src/dht/store/StoreRpcLocal.js.map +1 -1
- package/dist/src/helpers/PeerID.d.ts +1 -0
- package/dist/src/helpers/PeerID.js +7 -2
- package/dist/src/helpers/PeerID.js.map +1 -1
- package/dist/src/helpers/peerIdFromPeerDescriptor.js +2 -2
- package/dist/src/helpers/peerIdFromPeerDescriptor.js.map +1 -1
- package/package.json +5 -5
- package/src/connection/ConnectionLockRpcRemote.ts +1 -2
- package/src/connection/ConnectionManager.ts +16 -17
- package/src/connection/ConnectorFacade.ts +1 -4
- package/src/connection/ManagedConnection.ts +12 -1
- package/src/connection/connectivityChecker.ts +3 -2
- package/src/connection/websocket/ClientWebsocket.ts +5 -2
- package/src/connection/websocket/ServerWebsocket.ts +40 -25
- package/src/connection/websocket/WebsocketConnector.ts +23 -12
- package/src/connection/websocket/WebsocketConnectorRpcLocal.ts +9 -11
- package/src/connection/websocket/WebsocketConnectorRpcRemote.ts +5 -14
- package/src/connection/websocket/WebsocketServer.ts +20 -5
- package/src/dht/DhtNode.ts +32 -24
- package/src/dht/DhtNodeRpcLocal.ts +2 -9
- package/src/dht/PeerManager.ts +110 -36
- package/src/dht/contact/SortedContactList.ts +87 -44
- package/src/dht/discovery/DiscoverySession.ts +15 -14
- package/src/dht/discovery/PeerDiscovery.ts +37 -22
- package/src/dht/find/FindSession.ts +6 -1
- package/src/dht/find/Finder.ts +6 -7
- package/src/dht/routing/Router.ts +8 -4
- package/src/dht/routing/RoutingSession.ts +8 -8
- package/src/dht/store/StoreRpcLocal.ts +19 -7
- package/src/helpers/PeerID.ts +6 -2
- package/src/helpers/peerIdFromPeerDescriptor.ts +4 -4
- package/test/benchmark/Find.test.ts +1 -1
- package/test/benchmark/KademliaCorrectness.test.ts +1 -1
- package/test/benchmark/SortedContactListBenchmark.test.ts +150 -0
- package/test/benchmark/WebsocketServerMemoryLeak.test.ts +41 -0
- package/test/benchmark/kademlia-simulation/SimulationNode.ts +6 -1
- package/test/end-to-end/Layer0.test.ts +4 -4
- package/test/end-to-end/Layer0MixedConnectionTypes.test.ts +10 -10
- package/test/end-to-end/Layer0Webrtc-Layer1.test.ts +4 -4
- package/test/end-to-end/Layer1-Scale-WebSocket.test.ts +2 -2
- package/test/end-to-end/Layer1-Scale-Webrtc.test.ts +2 -2
- package/test/end-to-end/RecoveryFromFailedAutoCertification.test.ts +1 -1
- package/test/end-to-end/memory-leak.test.ts +1 -0
- package/test/integration/DhtJoinPeerDiscovery.test.ts +2 -2
- package/test/integration/Layer1-scale.test.ts +1 -1
- package/test/integration/Mock-Layer1-Layer0.test.ts +15 -15
- package/test/integration/MultipleEntryPointJoining.test.ts +7 -7
- package/test/integration/ReplicateData.test.ts +6 -1
- package/test/integration/SimultaneousConnections.test.ts +81 -49
- package/test/integration/Store.test.ts +1 -1
- package/test/integration/StoreOnDhtWithTwoNodes.test.ts +1 -1
- package/test/integration/WebrtcConnectionManagement.test.ts +29 -0
- package/test/integration/WebsocketConnectionManagement.test.ts +65 -4
- package/test/integration/WebsocketConnectorRpc.test.ts +3 -5
- package/test/unit/SortedContactList.test.ts +15 -10
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { Logger, runAndWaitForEvents3 } from '@streamr/utils'
|
|
2
2
|
import EventEmitter from 'eventemitter3'
|
|
3
3
|
import { v4 } from 'uuid'
|
|
4
|
-
import { PeerID } from '../../helpers/PeerID'
|
|
4
|
+
import { PeerID, PeerIDKey } from '../../helpers/PeerID'
|
|
5
5
|
import { PeerDescriptor } from '../../proto/packages/dht/protos/DhtRpc'
|
|
6
|
-
import {
|
|
6
|
+
import { PeerManager, getDistance } from '../PeerManager'
|
|
7
7
|
import { DhtNodeRpcRemote } from '../DhtNodeRpcRemote'
|
|
8
8
|
import { getNodeIdFromPeerDescriptor } from '../../helpers/peerIdFromPeerDescriptor'
|
|
9
|
-
import { PeerManager } from '../PeerManager'
|
|
10
9
|
|
|
11
10
|
const logger = new Logger(module)
|
|
12
11
|
|
|
@@ -16,10 +15,11 @@ interface DiscoverySessionEvents {
|
|
|
16
15
|
|
|
17
16
|
interface DiscoverySessionConfig {
|
|
18
17
|
targetId: Uint8Array
|
|
19
|
-
localPeerDescriptor: PeerDescriptor
|
|
20
18
|
parallelism: number
|
|
21
19
|
noProgressLimit: number
|
|
22
20
|
peerManager: PeerManager
|
|
21
|
+
// Note that contacted peers will be mutated by the DiscoverySession or other parallel sessions
|
|
22
|
+
contactedPeers: Set<PeerIDKey>
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export class DiscoverySession {
|
|
@@ -49,7 +49,7 @@ export class DiscoverySession {
|
|
|
49
49
|
}
|
|
50
50
|
logger.trace(`Getting closest peers from contact: ${getNodeIdFromPeerDescriptor(contact.getPeerDescriptor())}`)
|
|
51
51
|
this.outgoingClosestPeersRequestsCounter++
|
|
52
|
-
this.config.
|
|
52
|
+
this.config.contactedPeers.add(contact.getPeerId().toKey())
|
|
53
53
|
const returnedContacts = await contact.getClosestPeers(this.config.targetId)
|
|
54
54
|
this.config.peerManager.handlePeerActive(contact.getPeerId())
|
|
55
55
|
return returnedContacts
|
|
@@ -60,9 +60,12 @@ export class DiscoverySession {
|
|
|
60
60
|
return
|
|
61
61
|
}
|
|
62
62
|
this.ongoingClosestPeersRequests.delete(peerId.toKey())
|
|
63
|
-
const
|
|
63
|
+
const oldClosestNeighbor = this.config.peerManager.getClosestNeighborsTo(this.config.targetId, 1)[0]
|
|
64
|
+
const oldClosestDistance = getDistance(this.config.targetId, oldClosestNeighbor.getPeerId().value)
|
|
64
65
|
this.addNewContacts(contacts)
|
|
65
|
-
|
|
66
|
+
const newClosestNeighbor = this.config.peerManager.getClosestNeighborsTo(this.config.targetId, 1)[0]
|
|
67
|
+
const newClosestDistance = getDistance(this.config.targetId, newClosestNeighbor.getPeerId().value)
|
|
68
|
+
if (newClosestDistance >= oldClosestDistance) {
|
|
66
69
|
this.noProgressCounter++
|
|
67
70
|
} else {
|
|
68
71
|
this.noProgressCounter = 0
|
|
@@ -81,7 +84,7 @@ export class DiscoverySession {
|
|
|
81
84
|
if (this.stopped) {
|
|
82
85
|
return
|
|
83
86
|
}
|
|
84
|
-
const uncontacted = this.config.peerManager.
|
|
87
|
+
const uncontacted = this.config.peerManager.getClosestContactsTo(this.config.targetId, this.config.parallelism, this.config.contactedPeers)
|
|
85
88
|
if (uncontacted.length === 0 || this.noProgressCounter >= this.config.noProgressLimit) {
|
|
86
89
|
this.emitter.emit('discoveryCompleted')
|
|
87
90
|
this.stopped = true
|
|
@@ -103,18 +106,16 @@ export class DiscoverySession {
|
|
|
103
106
|
}
|
|
104
107
|
}
|
|
105
108
|
|
|
106
|
-
public async findClosestNodes(timeout: number): Promise<
|
|
107
|
-
if (this.config.peerManager.
|
|
108
|
-
|
|
109
|
-
+ this.config.peerManager.neighborList!.getSize())
|
|
110
|
-
return this.config.peerManager.neighborList!
|
|
109
|
+
public async findClosestNodes(timeout: number): Promise<void> {
|
|
110
|
+
if (this.config.peerManager.getNumberOfContacts(this.config.contactedPeers) === 0) {
|
|
111
|
+
return
|
|
111
112
|
}
|
|
113
|
+
// TODO add abortController and signal it in stop()
|
|
112
114
|
await runAndWaitForEvents3<DiscoverySessionEvents>(
|
|
113
115
|
[this.findMoreContacts.bind(this)],
|
|
114
116
|
[[this.emitter, 'discoveryCompleted']],
|
|
115
117
|
timeout
|
|
116
118
|
)
|
|
117
|
-
return this.config.peerManager.neighborList!
|
|
118
119
|
}
|
|
119
120
|
|
|
120
121
|
public stop(): void {
|
|
@@ -4,9 +4,10 @@ import { areEqualPeerDescriptors, getNodeIdFromPeerDescriptor, peerIdFromPeerDes
|
|
|
4
4
|
import { PeerDescriptor } from '../../proto/packages/dht/protos/DhtRpc'
|
|
5
5
|
import { Logger, scheduleAtInterval, setAbortableTimeout } from '@streamr/utils'
|
|
6
6
|
import { ConnectionManager } from '../../connection/ConnectionManager'
|
|
7
|
+
import { PeerManager } from '../PeerManager'
|
|
7
8
|
import { createRandomNodeId } from '../../helpers/nodeId'
|
|
8
9
|
import { ServiceID } from '../../types/ServiceID'
|
|
9
|
-
import {
|
|
10
|
+
import { PeerIDKey } from '../../helpers/PeerID'
|
|
10
11
|
|
|
11
12
|
interface PeerDiscoveryConfig {
|
|
12
13
|
localPeerDescriptor: PeerDescriptor
|
|
@@ -36,7 +37,27 @@ export class PeerDiscovery {
|
|
|
36
37
|
this.abortController = new AbortController()
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
async joinDht(
|
|
40
|
+
async joinDht(
|
|
41
|
+
entryPoints: PeerDescriptor[],
|
|
42
|
+
doAdditionalRandomPeerDiscovery = true,
|
|
43
|
+
retry = true
|
|
44
|
+
): Promise<void> {
|
|
45
|
+
const contactedPeers = new Set<PeerIDKey>()
|
|
46
|
+
await Promise.all(entryPoints.map((entryPoint) => this.joinThroughEntryPoint(
|
|
47
|
+
entryPoint,
|
|
48
|
+
contactedPeers,
|
|
49
|
+
doAdditionalRandomPeerDiscovery,
|
|
50
|
+
retry
|
|
51
|
+
)))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async joinThroughEntryPoint(
|
|
55
|
+
entryPointDescriptor: PeerDescriptor,
|
|
56
|
+
// Note that this set is mutated by DiscoverySession
|
|
57
|
+
contactedPeers: Set<PeerIDKey>,
|
|
58
|
+
doAdditionalRandomPeerDiscovery = true,
|
|
59
|
+
retry = true
|
|
60
|
+
): Promise<void> {
|
|
40
61
|
if (this.isStopped()) {
|
|
41
62
|
return
|
|
42
63
|
}
|
|
@@ -51,24 +72,22 @@ export class PeerDiscovery {
|
|
|
51
72
|
this.config.connectionManager?.lockConnection(entryPointDescriptor, `${this.config.serviceId}::joinDht`)
|
|
52
73
|
this.config.peerManager.handleNewPeers([entryPointDescriptor])
|
|
53
74
|
const targetId = peerIdFromPeerDescriptor(this.config.localPeerDescriptor).value
|
|
54
|
-
const
|
|
55
|
-
this.config.peerManager.neighborList!.addContacts(closest)
|
|
56
|
-
const sessions = [this.createSession(targetId)]
|
|
75
|
+
const sessions = [this.createSession(targetId, contactedPeers)]
|
|
57
76
|
if (doAdditionalRandomPeerDiscovery) {
|
|
58
|
-
sessions.push(this.createSession(createRandomNodeId()))
|
|
77
|
+
sessions.push(this.createSession(createRandomNodeId(), contactedPeers))
|
|
59
78
|
}
|
|
60
79
|
await this.runSessions(sessions, entryPointDescriptor, retry)
|
|
61
80
|
this.config.connectionManager?.unlockConnection(entryPointDescriptor, `${this.config.serviceId}::joinDht`)
|
|
62
81
|
|
|
63
82
|
}
|
|
64
83
|
|
|
65
|
-
private createSession(targetId: Uint8Array): DiscoverySession {
|
|
84
|
+
private createSession(targetId: Uint8Array, contactedPeers: Set<PeerIDKey>): DiscoverySession {
|
|
66
85
|
const sessionOptions = {
|
|
67
86
|
targetId,
|
|
68
|
-
localPeerDescriptor: this.config.localPeerDescriptor,
|
|
69
87
|
parallelism: this.config.parallelism,
|
|
70
88
|
noProgressLimit: this.config.joinNoProgressLimit,
|
|
71
|
-
peerManager: this.config.peerManager
|
|
89
|
+
peerManager: this.config.peerManager,
|
|
90
|
+
contactedPeers
|
|
72
91
|
}
|
|
73
92
|
return new DiscoverySession(sessionOptions)
|
|
74
93
|
}
|
|
@@ -83,7 +102,7 @@ export class PeerDiscovery {
|
|
|
83
102
|
logger.debug(`DHT join on ${this.config.serviceId} timed out`)
|
|
84
103
|
} finally {
|
|
85
104
|
if (!this.isStopped()) {
|
|
86
|
-
if (this.config.peerManager.
|
|
105
|
+
if (this.config.peerManager.getNumberOfNeighbors() === 0) {
|
|
87
106
|
if (retry) {
|
|
88
107
|
// TODO should we catch possible promise rejection?
|
|
89
108
|
setAbortableTimeout(() => this.rejoinDht(entryPointDescriptor), 1000, this.abortController.signal)
|
|
@@ -103,8 +122,7 @@ export class PeerDiscovery {
|
|
|
103
122
|
logger.debug(`Rejoining DHT ${this.config.serviceId}`)
|
|
104
123
|
this.rejoinOngoing = true
|
|
105
124
|
try {
|
|
106
|
-
this.
|
|
107
|
-
await this.joinDht(entryPoint)
|
|
125
|
+
await this.joinThroughEntryPoint(entryPoint, new Set())
|
|
108
126
|
logger.debug(`Rejoined DHT successfully ${this.config.serviceId}!`)
|
|
109
127
|
} catch (err) {
|
|
110
128
|
logger.warn(`Rejoining DHT ${this.config.serviceId} failed`)
|
|
@@ -128,16 +146,13 @@ export class PeerDiscovery {
|
|
|
128
146
|
if (this.isStopped()) {
|
|
129
147
|
return
|
|
130
148
|
}
|
|
131
|
-
const nodes = this.config.peerManager.
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const contacts = await peer.getClosestPeers(this.config.localPeerDescriptor.nodeId)
|
|
137
|
-
contacts.forEach((contact) => {
|
|
138
|
-
this.config.peerManager.handleNewPeers([contact])
|
|
149
|
+
const nodes = this.config.peerManager.getClosestNeighborsTo(this.config.localPeerDescriptor.nodeId, this.config.parallelism)
|
|
150
|
+
await Promise.allSettled(
|
|
151
|
+
nodes.map(async (peer: DhtNodeRpcRemote) => {
|
|
152
|
+
const contacts = await peer.getClosestPeers(this.config.localPeerDescriptor.nodeId!)
|
|
153
|
+
this.config.peerManager.handleNewPeers(contacts)
|
|
139
154
|
})
|
|
140
|
-
|
|
155
|
+
)
|
|
141
156
|
}
|
|
142
157
|
|
|
143
158
|
public isJoinOngoing(): boolean {
|
|
@@ -158,7 +173,7 @@ export class PeerDiscovery {
|
|
|
158
173
|
clearTimeout(this.rejoinTimeoutRef)
|
|
159
174
|
this.rejoinTimeoutRef = undefined
|
|
160
175
|
}
|
|
161
|
-
this.ongoingDiscoverySessions.forEach((session
|
|
176
|
+
this.ongoingDiscoverySessions.forEach((session) => {
|
|
162
177
|
session.stop()
|
|
163
178
|
})
|
|
164
179
|
}
|
|
@@ -46,7 +46,12 @@ export class FindSession extends EventEmitter<FindSessionEvents> {
|
|
|
46
46
|
this.nodeIdToFind = config.nodeIdToFind
|
|
47
47
|
this.localPeerId = config.localPeerId
|
|
48
48
|
this.waitedRoutingPathCompletions = config.waitedRoutingPathCompletions
|
|
49
|
-
this.results = new SortedContactList(
|
|
49
|
+
this.results = new SortedContactList({
|
|
50
|
+
referenceId: PeerID.fromValue(this.nodeIdToFind),
|
|
51
|
+
maxSize: 10, // TODO use config option or named constant?
|
|
52
|
+
allowToContainReferenceId: true,
|
|
53
|
+
emitEvents: false
|
|
54
|
+
})
|
|
50
55
|
this.action = config.action
|
|
51
56
|
this.rpcCommunicator = new ListeningRpcCommunicator(this.serviceId, this.transport, {
|
|
52
57
|
rpcRequestTimeout: 15000
|
package/src/dht/find/Finder.ts
CHANGED
|
@@ -275,13 +275,12 @@ export class Finder implements IFinder {
|
|
|
275
275
|
|
|
276
276
|
private getClosestConnections(nodeId: Uint8Array, limit: number): PeerDescriptor[] {
|
|
277
277
|
const connectedPeers = Array.from(this.connections.values())
|
|
278
|
-
const closestPeers = new SortedContactList<DhtNodeRpcRemote>(
|
|
279
|
-
PeerID.fromValue(nodeId),
|
|
280
|
-
limit,
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
)
|
|
278
|
+
const closestPeers = new SortedContactList<DhtNodeRpcRemote>({
|
|
279
|
+
referenceId: PeerID.fromValue(nodeId),
|
|
280
|
+
maxSize: limit,
|
|
281
|
+
allowToContainReferenceId: true,
|
|
282
|
+
emitEvents: false
|
|
283
|
+
})
|
|
285
284
|
closestPeers.addContacts(connectedPeers)
|
|
286
285
|
return closestPeers.getClosestContacts(limit).map((peer) => peer.getPeerDescriptor())
|
|
287
286
|
}
|
|
@@ -134,13 +134,13 @@ export class Router implements IRouter {
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
public doRouteMessage(routedMessage: RouteMessageWrapper, mode = RoutingMode.ROUTE): RouteMessageAck {
|
|
137
|
+
public doRouteMessage(routedMessage: RouteMessageWrapper, mode = RoutingMode.ROUTE, excludedPeer?: PeerDescriptor): RouteMessageAck {
|
|
138
138
|
if (this.stopped) {
|
|
139
139
|
return createRouteMessageAck(routedMessage, RouteMessageError.STOPPED)
|
|
140
140
|
}
|
|
141
141
|
logger.trace(`Routing message ${routedMessage.requestId} from ${getNodeIdFromPeerDescriptor(routedMessage.sourcePeer!)} `
|
|
142
142
|
+ `to ${getNodeIdFromPeerDescriptor(routedMessage.destinationPeer!)}`)
|
|
143
|
-
const session = this.createRoutingSession(routedMessage, mode)
|
|
143
|
+
const session = this.createRoutingSession(routedMessage, mode, excludedPeer)
|
|
144
144
|
const contacts = session.updateAndGetRoutablePeers()
|
|
145
145
|
if (contacts.length > 0) {
|
|
146
146
|
this.addRoutingSession(session)
|
|
@@ -173,7 +173,11 @@ export class Router implements IRouter {
|
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
private createRoutingSession(routedMessage: RouteMessageWrapper, mode: RoutingMode): RoutingSession {
|
|
176
|
+
private createRoutingSession(routedMessage: RouteMessageWrapper, mode: RoutingMode, excludedPeer?: PeerDescriptor): RoutingSession {
|
|
177
|
+
const excludedPeers = routedMessage.routingPath.map((descriptor) => peerIdFromPeerDescriptor(descriptor))
|
|
178
|
+
if (excludedPeer) {
|
|
179
|
+
excludedPeers.push(peerIdFromPeerDescriptor(excludedPeer))
|
|
180
|
+
}
|
|
177
181
|
logger.trace('routing session created with connections: ' + this.connections.size)
|
|
178
182
|
return new RoutingSession(
|
|
179
183
|
this.rpcCommunicator,
|
|
@@ -182,7 +186,7 @@ export class Router implements IRouter {
|
|
|
182
186
|
this.connections,
|
|
183
187
|
areEqualPeerDescriptors(this.localPeerDescriptor, routedMessage.sourcePeer!) ? 2 : 1,
|
|
184
188
|
mode,
|
|
185
|
-
|
|
189
|
+
excludedPeers
|
|
186
190
|
)
|
|
187
191
|
}
|
|
188
192
|
|
|
@@ -98,14 +98,14 @@ export class RoutingSession extends EventEmitter<RoutingSessionEvents> {
|
|
|
98
98
|
this.mode = mode
|
|
99
99
|
const previousPeer = getPreviousPeer(messageToRoute)
|
|
100
100
|
const previousId = previousPeer ? PeerID.fromValue(previousPeer.nodeId) : undefined
|
|
101
|
-
this.contactList = new SortedContactList(
|
|
102
|
-
PeerID.fromValue(this.messageToRoute.destinationPeer!.nodeId),
|
|
103
|
-
10000,
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
)
|
|
101
|
+
this.contactList = new SortedContactList({
|
|
102
|
+
referenceId: PeerID.fromValue(this.messageToRoute.destinationPeer!.nodeId),
|
|
103
|
+
maxSize: 10000, // TODO use config option or named constant?
|
|
104
|
+
allowToContainReferenceId: true,
|
|
105
|
+
peerIdDistanceLimit: previousId,
|
|
106
|
+
excludedPeerIDs: excludedPeerIDs,
|
|
107
|
+
emitEvents: false
|
|
108
|
+
})
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
private onRequestFailed(peerId: PeerID) {
|
|
@@ -93,8 +93,12 @@ export class StoreRpcLocal implements IStoreRpc {
|
|
|
93
93
|
const localPeerId = PeerID.fromValue(this.localPeerDescriptor.nodeId)
|
|
94
94
|
|
|
95
95
|
const closestToData = this.getNodesClosestToIdFromBucket(dataEntry.key, 10)
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
const sortedList = new SortedContactList<Contact>({
|
|
97
|
+
referenceId: PeerID.fromValue(dataEntry.key),
|
|
98
|
+
maxSize: 20, // TODO use config option or named constant?
|
|
99
|
+
allowToContainReferenceId: true,
|
|
100
|
+
emitEvents: false
|
|
101
|
+
})
|
|
98
102
|
sortedList.addContact(new Contact(this.localPeerDescriptor))
|
|
99
103
|
|
|
100
104
|
closestToData.forEach((con) => {
|
|
@@ -108,6 +112,7 @@ export class StoreRpcLocal implements IStoreRpc {
|
|
|
108
112
|
return false
|
|
109
113
|
}
|
|
110
114
|
|
|
115
|
+
this.localDataStore.setStale(dataId, dataEntry.creator!, false)
|
|
111
116
|
const newPeerId = PeerID.fromValue(newNode.nodeId)
|
|
112
117
|
sortedList.addContact(new Contact(newNode))
|
|
113
118
|
|
|
@@ -125,10 +130,8 @@ export class StoreRpcLocal implements IStoreRpc {
|
|
|
125
130
|
// do replicate data to it
|
|
126
131
|
|
|
127
132
|
if (index < this.redundancyFactor) {
|
|
128
|
-
this.localDataStore.setStale(dataId, dataEntry.creator!, false)
|
|
129
133
|
return true
|
|
130
134
|
} else {
|
|
131
|
-
this.localDataStore.setStale(dataId, dataEntry.creator!, true)
|
|
132
135
|
return false
|
|
133
136
|
}
|
|
134
137
|
}
|
|
@@ -201,7 +204,12 @@ export class StoreRpcLocal implements IStoreRpc {
|
|
|
201
204
|
private selfIsOneOfClosestPeers(dataId: Uint8Array): boolean {
|
|
202
205
|
const localPeerId = PeerID.fromValue(this.localPeerDescriptor.nodeId)
|
|
203
206
|
const closestPeers = this.getNodesClosestToIdFromBucket(dataId, this.redundancyFactor)
|
|
204
|
-
const sortedList = new SortedContactList<Contact>(
|
|
207
|
+
const sortedList = new SortedContactList<Contact>({
|
|
208
|
+
referenceId: localPeerId,
|
|
209
|
+
maxSize: this.redundancyFactor,
|
|
210
|
+
allowToContainReferenceId: true,
|
|
211
|
+
emitEvents: false
|
|
212
|
+
})
|
|
205
213
|
sortedList.addContact(new Contact(this.localPeerDescriptor))
|
|
206
214
|
closestPeers.forEach((con) => sortedList.addContact(new Contact(con.getPeerDescriptor())))
|
|
207
215
|
return sortedList.getClosestContacts().some((node) => node.getPeerId().equals(localPeerId))
|
|
@@ -282,8 +290,12 @@ export class StoreRpcLocal implements IStoreRpc {
|
|
|
282
290
|
const dataId = PeerID.fromValue(dataEntry.key)
|
|
283
291
|
const incomingPeerId = PeerID.fromValue(incomingPeer.nodeId)
|
|
284
292
|
const closestToData = this.getNodesClosestToIdFromBucket(dataEntry.key, 10)
|
|
285
|
-
|
|
286
|
-
|
|
293
|
+
const sortedList = new SortedContactList<Contact>({
|
|
294
|
+
referenceId: dataId,
|
|
295
|
+
maxSize: this.redundancyFactor,
|
|
296
|
+
allowToContainReferenceId: true,
|
|
297
|
+
emitEvents: false
|
|
298
|
+
})
|
|
287
299
|
sortedList.addContact(new Contact(this.localPeerDescriptor))
|
|
288
300
|
|
|
289
301
|
closestToData.forEach((con) => {
|
package/src/helpers/PeerID.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { BrandedString } from '@streamr/utils'
|
|
1
|
+
import { BrandedString, binaryToHex } from '@streamr/utils'
|
|
2
2
|
import { UUID } from './UUID'
|
|
3
3
|
import { IllegalArguments } from './errors'
|
|
4
4
|
import crypto from 'crypto'
|
|
5
5
|
|
|
6
6
|
export type PeerIDKey = BrandedString<'PeerIDKey'>
|
|
7
7
|
|
|
8
|
+
export const createPeerIDKey = (nodeId: Uint8Array): PeerIDKey => {
|
|
9
|
+
return binaryToHex(nodeId) as PeerIDKey
|
|
10
|
+
}
|
|
11
|
+
|
|
8
12
|
export class PeerID {
|
|
9
13
|
// avoid creating a new instance for every operation
|
|
10
14
|
private static readonly textEncoder = new TextEncoder()
|
|
@@ -30,7 +34,7 @@ export class PeerID {
|
|
|
30
34
|
throw new IllegalArguments('Constructor of PeerID must be given either ip, value or stringValue')
|
|
31
35
|
}
|
|
32
36
|
|
|
33
|
-
this.key =
|
|
37
|
+
this.key = createPeerIDKey(this.data)
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
static fromIp(ip: string): PeerID {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { binaryToHex } from '@streamr/utils'
|
|
1
|
+
import { areEqualBinaries, binaryToHex } from '@streamr/utils'
|
|
2
2
|
import { PeerDescriptor } from '../proto/packages/dht/protos/DhtRpc'
|
|
3
|
-
import { PeerID, PeerIDKey } from './PeerID'
|
|
3
|
+
import { PeerID, PeerIDKey, createPeerIDKey } from './PeerID'
|
|
4
4
|
|
|
5
5
|
export const peerIdFromPeerDescriptor = (peerDescriptor: PeerDescriptor): PeerID => {
|
|
6
6
|
return PeerID.fromValue(peerDescriptor.nodeId)
|
|
@@ -12,9 +12,9 @@ export const getNodeIdFromPeerDescriptor = (peerDescriptor: PeerDescriptor): str
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export const keyFromPeerDescriptor = (peerDescriptor: PeerDescriptor): PeerIDKey => {
|
|
15
|
-
return
|
|
15
|
+
return createPeerIDKey(peerDescriptor.nodeId)
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export const areEqualPeerDescriptors = (peerDescriptor1: PeerDescriptor, peerDescriptor2: PeerDescriptor): boolean => {
|
|
19
|
-
return
|
|
19
|
+
return areEqualBinaries(peerDescriptor1.nodeId, peerDescriptor2.nodeId)
|
|
20
20
|
}
|
|
@@ -71,7 +71,7 @@ describe('Find correctness', () => {
|
|
|
71
71
|
logger.info('waiting over')
|
|
72
72
|
|
|
73
73
|
nodes.forEach((node) => logger.info(getNodeIdFromPeerDescriptor(node.getLocalPeerDescriptor()) + ': connections:' +
|
|
74
|
-
node.getNumberOfConnections() + ', kbucket: ' + node.
|
|
74
|
+
node.getNumberOfConnections() + ', kbucket: ' + node.getNumberOfNeighbors()
|
|
75
75
|
+ ', localLocked: ' + node.getNumberOfLocalLockedConnections()
|
|
76
76
|
+ ', remoteLocked: ' + node.getNumberOfRemoteLockedConnections()
|
|
77
77
|
+ ', weakLocked: ' + node.getNumberOfWeakLockedConnections()))
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
|
|
3
|
+
import KBucket from 'k-bucket'
|
|
4
|
+
import { SortedContactList } from '../../src/dht/contact/SortedContactList'
|
|
5
|
+
import { PeerID } from '../../src/helpers/PeerID'
|
|
6
|
+
import crypto from 'crypto'
|
|
7
|
+
|
|
8
|
+
const NUM_ADDS = 1000
|
|
9
|
+
interface Item {
|
|
10
|
+
id: Uint8Array
|
|
11
|
+
vectorClock: number
|
|
12
|
+
getPeerId: () => PeerID
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const createRandomItem = (index: number): Item => {
|
|
16
|
+
const rand = new Uint8Array(crypto.randomBytes(20))
|
|
17
|
+
return {
|
|
18
|
+
getPeerId: () => PeerID.fromValue(rand),
|
|
19
|
+
id: rand,
|
|
20
|
+
vectorClock: index
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function shuffleArray<T>(array: T[]): T[] {
|
|
25
|
+
for (let i = array.length - 1; i > 0; i--) {
|
|
26
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
27
|
+
[array[i], array[j]] = [array[j], array[i]]
|
|
28
|
+
}
|
|
29
|
+
return array
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe('SortedContactListBenchmark', () => {
|
|
33
|
+
|
|
34
|
+
it('adds ' + NUM_ADDS + ' random peerIDs', async () => {
|
|
35
|
+
const randomIds = []
|
|
36
|
+
for (let i = 0; i < NUM_ADDS; i++) {
|
|
37
|
+
randomIds.push(createRandomItem(i))
|
|
38
|
+
}
|
|
39
|
+
const list = new SortedContactList({
|
|
40
|
+
referenceId: PeerID.fromValue(crypto.randomBytes(20)),
|
|
41
|
+
allowToContainReferenceId: true,
|
|
42
|
+
emitEvents: true
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
console.time('SortedContactList.addContact() with emitEvents=true')
|
|
46
|
+
for (let i = 0; i < NUM_ADDS; i++) {
|
|
47
|
+
list.addContact(randomIds[i])
|
|
48
|
+
}
|
|
49
|
+
console.timeEnd('SortedContactList.addContact() with emitEvents=true')
|
|
50
|
+
|
|
51
|
+
const list2 = new SortedContactList({
|
|
52
|
+
referenceId: PeerID.fromValue(crypto.randomBytes(20)),
|
|
53
|
+
allowToContainReferenceId: true,
|
|
54
|
+
emitEvents: false
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
console.time('SortedContactList.addContact() with emitEvents=false')
|
|
58
|
+
for (let i = 0; i < NUM_ADDS; i++) {
|
|
59
|
+
list2.addContact(randomIds[i])
|
|
60
|
+
}
|
|
61
|
+
console.timeEnd('SortedContactList.addContact() with emitEvents=false')
|
|
62
|
+
|
|
63
|
+
const kBucket = new KBucket<Item>({ localNodeId: crypto.randomBytes(20) })
|
|
64
|
+
console.time('KBucket.add()')
|
|
65
|
+
for (let i = 0; i < NUM_ADDS; i++) {
|
|
66
|
+
kBucket.add(randomIds[i])
|
|
67
|
+
}
|
|
68
|
+
console.timeEnd('KBucket.add()')
|
|
69
|
+
|
|
70
|
+
console.time('kBucket toArray()')
|
|
71
|
+
|
|
72
|
+
for (let i = 0; i < NUM_ADDS; i++) {
|
|
73
|
+
kBucket.toArray()
|
|
74
|
+
}
|
|
75
|
+
console.timeEnd('kBucket toArray()')
|
|
76
|
+
|
|
77
|
+
console.time('kBucket closest()')
|
|
78
|
+
for (let i = 0; i < NUM_ADDS; i++) {
|
|
79
|
+
kBucket.closest(crypto.randomBytes(20), 20)
|
|
80
|
+
}
|
|
81
|
+
console.timeEnd('kBucket closest()')
|
|
82
|
+
|
|
83
|
+
console.time('SortedContactList.getClosestContacts() with emitEvents=true')
|
|
84
|
+
for (let i = 0; i < NUM_ADDS; i++) {
|
|
85
|
+
const closest = new SortedContactList<Item>({
|
|
86
|
+
referenceId: PeerID.fromValue(crypto.randomBytes(20)),
|
|
87
|
+
allowToContainReferenceId: true,
|
|
88
|
+
emitEvents: true
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
const arrayFromBucket = kBucket.toArray()
|
|
92
|
+
arrayFromBucket.map((contact) => closest.addContact(contact))
|
|
93
|
+
closest.getClosestContacts(20)
|
|
94
|
+
}
|
|
95
|
+
console.timeEnd('SortedContactList.getClosestContacts() with emitEvents=true')
|
|
96
|
+
|
|
97
|
+
console.time('SortedContactList.getClosestContacts() with emitEvents=false')
|
|
98
|
+
for (let i = 0; i < NUM_ADDS; i++) {
|
|
99
|
+
const closest = new SortedContactList<Item>({
|
|
100
|
+
referenceId: PeerID.fromValue(crypto.randomBytes(20)),
|
|
101
|
+
allowToContainReferenceId: true,
|
|
102
|
+
emitEvents: false
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const arrayFromBucket = kBucket.toArray()
|
|
106
|
+
arrayFromBucket.map((contact) => closest.addContact(contact))
|
|
107
|
+
closest.getClosestContacts(20)
|
|
108
|
+
}
|
|
109
|
+
console.timeEnd('SortedContactList.getClosestContacts() with emitEvents=false')
|
|
110
|
+
|
|
111
|
+
console.time('SortedContactList.getClosestContacts() with emitEvents=false and lodash')
|
|
112
|
+
for (let i = 0; i < NUM_ADDS; i++) {
|
|
113
|
+
const closest = new SortedContactList<Item>({
|
|
114
|
+
referenceId: PeerID.fromValue(crypto.randomBytes(20)),
|
|
115
|
+
allowToContainReferenceId: true,
|
|
116
|
+
emitEvents: false
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
const arrayFromBucket = kBucket.toArray()
|
|
120
|
+
arrayFromBucket.map((contact) => closest.addContact(contact))
|
|
121
|
+
closest.getClosestContacts(20)
|
|
122
|
+
}
|
|
123
|
+
console.timeEnd('SortedContactList.getClosestContacts() with emitEvents=false and lodash')
|
|
124
|
+
|
|
125
|
+
console.time('SortedContactList.getClosestContacts() with emitEvents=false and addContacts()')
|
|
126
|
+
for (let i = 0; i < NUM_ADDS; i++) {
|
|
127
|
+
const closest = new SortedContactList<Item>({
|
|
128
|
+
referenceId: PeerID.fromValue(crypto.randomBytes(20)),
|
|
129
|
+
allowToContainReferenceId: true,
|
|
130
|
+
emitEvents: false
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
const arrayFromBucket = kBucket.toArray()
|
|
134
|
+
closest.addContacts(arrayFromBucket)
|
|
135
|
+
closest.getClosestContacts(20)
|
|
136
|
+
}
|
|
137
|
+
console.timeEnd('SortedContactList.getClosestContacts() with emitEvents=false and addContacts()')
|
|
138
|
+
|
|
139
|
+
const shuffled = shuffleArray(kBucket.toArray())
|
|
140
|
+
console.time('kbucket add and closest')
|
|
141
|
+
for (let i = 0; i < NUM_ADDS; i++) {
|
|
142
|
+
const bucket2 = new KBucket<Item>({ localNodeId: crypto.randomBytes(20) })
|
|
143
|
+
|
|
144
|
+
shuffled.map((contact) => bucket2.add(contact))
|
|
145
|
+
bucket2.closest(crypto.randomBytes(20), 20)
|
|
146
|
+
}
|
|
147
|
+
console.timeEnd('kbucket add and closest')
|
|
148
|
+
|
|
149
|
+
})
|
|
150
|
+
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
|
|
3
|
+
import { wait } from '@streamr/utils'
|
|
4
|
+
import { WebsocketServer } from '../../src/connection/websocket/WebsocketServer'
|
|
5
|
+
import { ClientWebsocket } from '../../src/exports'
|
|
6
|
+
|
|
7
|
+
// This 'test' is meant to be run manually using the following command:
|
|
8
|
+
// node --inspect ../../../../node_modules/.bin/jest WebsocketServerMemoryLeak.test.ts
|
|
9
|
+
// while wathing for memory leaks in Chrome DevTools
|
|
10
|
+
|
|
11
|
+
describe('WebsocketServermemoryLeak', () => {
|
|
12
|
+
|
|
13
|
+
it('Accepts and detroys connections', async () => {
|
|
14
|
+
const server = new WebsocketServer({
|
|
15
|
+
portRange: { min: 19792, max: 19792 },
|
|
16
|
+
enableTls: false
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
server.on('connected', (connection) => {
|
|
20
|
+
console.log('ServerWebsocket connected')
|
|
21
|
+
connection.destroy()
|
|
22
|
+
console.log('ServerWebsocket destroyed')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const port = await server.start()
|
|
26
|
+
expect(port).toEqual(19792)
|
|
27
|
+
|
|
28
|
+
for (let i = 0; i < 10000; i++) {
|
|
29
|
+
const clientWebsocket: ClientWebsocket = new ClientWebsocket()
|
|
30
|
+
clientWebsocket.on('connected', () => {
|
|
31
|
+
console.log('clientWebsocket connected ' + i)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
clientWebsocket.connect(`ws://127.0.0.1:${port}`)
|
|
35
|
+
i++
|
|
36
|
+
await wait(3000)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
await server.stop()
|
|
40
|
+
}, 120000000)
|
|
41
|
+
})
|
|
@@ -26,7 +26,12 @@ export class SimulationNode {
|
|
|
26
26
|
numberOfNodesPerKBucket: this.numberOfNodesPerKBucket
|
|
27
27
|
})
|
|
28
28
|
|
|
29
|
-
this.neighborList = new SortedContactList(
|
|
29
|
+
this.neighborList = new SortedContactList({
|
|
30
|
+
referenceId: this.ownId,
|
|
31
|
+
maxSize: 1000,
|
|
32
|
+
allowToContainReferenceId: false,
|
|
33
|
+
emitEvents: false
|
|
34
|
+
})
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
// For simulation use
|
|
@@ -71,9 +71,9 @@ describe('Layer0', () => {
|
|
|
71
71
|
node4.joinDht([epPeerDescriptor])
|
|
72
72
|
])
|
|
73
73
|
|
|
74
|
-
expect(node1.
|
|
75
|
-
expect(node2.
|
|
76
|
-
expect(node3.
|
|
77
|
-
expect(node4.
|
|
74
|
+
expect(node1.getNumberOfNeighbors()).toBeGreaterThanOrEqual(2)
|
|
75
|
+
expect(node2.getNumberOfNeighbors()).toBeGreaterThanOrEqual(2)
|
|
76
|
+
expect(node3.getNumberOfNeighbors()).toBeGreaterThanOrEqual(2)
|
|
77
|
+
expect(node4.getNumberOfNeighbors()).toBeGreaterThanOrEqual(2)
|
|
78
78
|
}, 10000)
|
|
79
79
|
})
|