@streamr/dht 100.0.0-testnet-one.0 → 100.0.0-testnet-one.1
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 +1 -2
- package/dist/src/connection/ConnectorFacade.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 +30 -19
- 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 +2 -5
- package/dist/src/dht/discovery/DiscoverySession.js +12 -9
- package/dist/src/dht/discovery/DiscoverySession.js.map +1 -1
- package/dist/src/dht/discovery/PeerDiscovery.d.ts +1 -1
- package/dist/src/dht/discovery/PeerDiscovery.js +4 -10
- 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 +0 -3
- 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 +31 -21
- 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 +14 -14
- package/src/dht/discovery/PeerDiscovery.ts +9 -16
- 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/StoreOnDhtWithTwoNodes.test.ts +1 -1
- package/test/integration/WebsocketConnectionManagement.test.ts +41 -3
- package/test/integration/WebsocketConnectorRpc.test.ts +3 -5
- package/test/unit/SortedContactList.test.ts +15 -10
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ServerCallContext } from '@protobuf-ts/runtime-rpc'
|
|
2
2
|
import { Logger } from '@streamr/utils'
|
|
3
|
-
import KBucket from 'k-bucket'
|
|
4
3
|
import { getNodeIdFromPeerDescriptor } from '../helpers/peerIdFromPeerDescriptor'
|
|
5
4
|
import { Empty } from '../proto/google/protobuf/empty'
|
|
6
5
|
import {
|
|
@@ -13,12 +12,11 @@ import {
|
|
|
13
12
|
} from '../proto/packages/dht/protos/DhtRpc'
|
|
14
13
|
import { IDhtNodeRpc } from '../proto/packages/dht/protos/DhtRpc.server'
|
|
15
14
|
import { DhtCallContext } from '../rpc-protocol/DhtCallContext'
|
|
16
|
-
import { DhtNodeRpcRemote } from './DhtNodeRpcRemote'
|
|
17
15
|
|
|
18
16
|
interface DhtNodeRpcLocalConfig {
|
|
19
|
-
bucket: KBucket<DhtNodeRpcRemote>
|
|
20
17
|
serviceId: string
|
|
21
18
|
peerDiscoveryQueryBatchSize: number
|
|
19
|
+
getClosestPeersTo: (nodeId: Uint8Array, limit: number) => PeerDescriptor[]
|
|
22
20
|
addNewContact: (contact: PeerDescriptor) => void
|
|
23
21
|
removeContact: (contact: PeerDescriptor) => void
|
|
24
22
|
}
|
|
@@ -36,17 +34,12 @@ export class DhtNodeRpcLocal implements IDhtNodeRpc {
|
|
|
36
34
|
async getClosestPeers(request: ClosestPeersRequest, context: ServerCallContext): Promise<ClosestPeersResponse> {
|
|
37
35
|
this.config.addNewContact((context as DhtCallContext).incomingSourceDescriptor!)
|
|
38
36
|
const response = {
|
|
39
|
-
peers: this.
|
|
37
|
+
peers: this.config.getClosestPeersTo(request.nodeId, this.config.peerDiscoveryQueryBatchSize),
|
|
40
38
|
requestId: request.requestId
|
|
41
39
|
}
|
|
42
40
|
return response
|
|
43
41
|
}
|
|
44
42
|
|
|
45
|
-
private getClosestPeerDescriptors(nodeId: Uint8Array, limit: number): PeerDescriptor[] {
|
|
46
|
-
const closestPeers = this.config.bucket.closest(nodeId, limit)
|
|
47
|
-
return closestPeers.map((rpcRemote: DhtNodeRpcRemote) => rpcRemote.getPeerDescriptor())
|
|
48
|
-
}
|
|
49
|
-
|
|
50
43
|
async ping(request: PingRequest, context: ServerCallContext): Promise<PingResponse> {
|
|
51
44
|
logger.trace('received ping request: ' + getNodeIdFromPeerDescriptor((context as DhtCallContext).incomingSourceDescriptor!))
|
|
52
45
|
setImmediate(() => {
|
package/src/dht/PeerManager.ts
CHANGED
|
@@ -21,7 +21,7 @@ const logger = new Logger(module)
|
|
|
21
21
|
|
|
22
22
|
interface PeerManagerConfig {
|
|
23
23
|
numberOfNodesPerKBucket: number
|
|
24
|
-
|
|
24
|
+
maxContactListSize: number
|
|
25
25
|
peerDiscoveryQueryBatchSize: number
|
|
26
26
|
ownPeerId: PeerID
|
|
27
27
|
connectionManager: ConnectionManager
|
|
@@ -37,14 +37,24 @@ export interface PeerManagerEvents {
|
|
|
37
37
|
kBucketEmpty: () => void
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
export const getDistance = (peerId1: Uint8Array, peerId2: Uint8Array): number => {
|
|
41
|
+
return KBucket.distance(peerId1, peerId2)
|
|
42
|
+
}
|
|
43
|
+
|
|
40
44
|
export class PeerManager extends EventEmitter<PeerManagerEvents> {
|
|
41
45
|
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
//
|
|
46
|
+
// Glossary:
|
|
47
|
+
// * 'neighbors' are the nodes that are our neighbors according to
|
|
48
|
+
// the protocol of the layer we are in
|
|
49
|
+
// * 'connections' are the nodes that are connected to this node on Layer0
|
|
50
|
+
// * 'contacts' are all non-unresponsive nodes that we know about
|
|
51
|
+
|
|
52
|
+
// The kademlia k-bucket
|
|
53
|
+
private bucket?: KBucket<DhtNodeRpcRemote>
|
|
54
|
+
// Nodes that are connected to this node on Layer0
|
|
45
55
|
public readonly connections: Map<PeerIDKey, DhtNodeRpcRemote> = new Map()
|
|
46
|
-
//
|
|
47
|
-
|
|
56
|
+
// All nodes that we know about
|
|
57
|
+
private contacts?: SortedContactList<DhtNodeRpcRemote>
|
|
48
58
|
private randomPeers?: RandomContactList<DhtNodeRpcRemote>
|
|
49
59
|
private readonly config: PeerManagerConfig
|
|
50
60
|
private stopped: boolean = false
|
|
@@ -61,24 +71,30 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
|
|
|
61
71
|
numberOfNodesPerKBucket: this.config.numberOfNodesPerKBucket,
|
|
62
72
|
numberOfNodesToPing: this.config.numberOfNodesPerKBucket
|
|
63
73
|
})
|
|
74
|
+
|
|
64
75
|
this.bucket.on('ping', (oldContacts: DhtNodeRpcRemote[], newContact: DhtNodeRpcRemote) => this.onKBucketPing(oldContacts, newContact))
|
|
65
76
|
this.bucket.on('removed', (contact: DhtNodeRpcRemote) => this.onKBucketRemoved(contact))
|
|
66
77
|
this.bucket.on('added', (contact: DhtNodeRpcRemote) => this.onKBucketAdded(contact))
|
|
67
78
|
this.bucket.on('updated', () => {
|
|
68
79
|
// TODO: Update contact info to the connection manager and reconnect
|
|
69
80
|
})
|
|
70
|
-
this.
|
|
71
|
-
|
|
81
|
+
this.contacts = new SortedContactList({
|
|
82
|
+
referenceId: this.config.ownPeerId,
|
|
83
|
+
maxSize: this.config.maxContactListSize,
|
|
84
|
+
allowToContainReferenceId: false,
|
|
85
|
+
emitEvents: true
|
|
86
|
+
})
|
|
87
|
+
this.contacts.on('contactRemoved', (removedContact: DhtNodeRpcRemote, activeContacts: DhtNodeRpcRemote[]) => {
|
|
72
88
|
if (this.stopped) {
|
|
73
89
|
return
|
|
74
90
|
}
|
|
75
91
|
this.emit('contactRemoved', removedContact.getPeerDescriptor(), activeContacts.map((c) => c.getPeerDescriptor()))
|
|
76
92
|
this.randomPeers!.addContact(this.config.createDhtNodeRpcRemote(removedContact.getPeerDescriptor()))
|
|
77
93
|
})
|
|
78
|
-
this.
|
|
94
|
+
this.contacts.on('newContact', (newContact: DhtNodeRpcRemote, activeContacts: DhtNodeRpcRemote[]) =>
|
|
79
95
|
this.emit('newContact', newContact.getPeerDescriptor(), activeContacts.map((c) => c.getPeerDescriptor()))
|
|
80
96
|
)
|
|
81
|
-
this.randomPeers = new RandomContactList(this.config.ownPeerId, this.config.
|
|
97
|
+
this.randomPeers = new RandomContactList(this.config.ownPeerId, this.config.maxContactListSize)
|
|
82
98
|
this.randomPeers.on('contactRemoved', (removedContact: DhtNodeRpcRemote, activeContacts: DhtNodeRpcRemote[]) =>
|
|
83
99
|
this.emit('randomContactRemoved', removedContact.getPeerDescriptor(), activeContacts.map((c) => c.getPeerDescriptor()))
|
|
84
100
|
)
|
|
@@ -91,11 +107,16 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
|
|
|
91
107
|
if (this.stopped) {
|
|
92
108
|
return
|
|
93
109
|
}
|
|
94
|
-
const sortingList: SortedContactList<DhtNodeRpcRemote> = new SortedContactList(
|
|
110
|
+
const sortingList: SortedContactList<DhtNodeRpcRemote> = new SortedContactList({
|
|
111
|
+
referenceId: this.config.ownPeerId,
|
|
112
|
+
maxSize: 100, // TODO use config option or named constant?
|
|
113
|
+
allowToContainReferenceId: false,
|
|
114
|
+
emitEvents: false
|
|
115
|
+
})
|
|
95
116
|
sortingList.addContacts(oldContacts)
|
|
96
117
|
const sortedContacts = sortingList.getAllContacts()
|
|
97
118
|
this.config.connectionManager?.weakUnlockConnection(sortedContacts[sortedContacts.length - 1].getPeerDescriptor())
|
|
98
|
-
this.bucket?.remove(sortedContacts[sortedContacts.length - 1].getPeerId().value)
|
|
119
|
+
this.bucket?.remove(sortedContacts[sortedContacts.length - 1].getPeerId().value)
|
|
99
120
|
this.bucket!.add(newContact)
|
|
100
121
|
}
|
|
101
122
|
|
|
@@ -151,9 +172,9 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
|
|
|
151
172
|
}
|
|
152
173
|
|
|
153
174
|
private getClosestActiveContactNotInBucket(): DhtNodeRpcRemote | undefined {
|
|
154
|
-
for (const contactId of this.
|
|
155
|
-
if (!this.bucket!.get(contactId.value) && this.
|
|
156
|
-
return this.
|
|
175
|
+
for (const contactId of this.contacts!.getContactIds()) {
|
|
176
|
+
if (!this.bucket!.get(contactId.value) && this.contacts!.isActive(contactId)) {
|
|
177
|
+
return this.contacts!.getContact(contactId)!.contact
|
|
157
178
|
}
|
|
158
179
|
}
|
|
159
180
|
return undefined
|
|
@@ -198,7 +219,7 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
|
|
|
198
219
|
logger.trace(`Removing contact ${getNodeIdFromPeerDescriptor(contact)}`)
|
|
199
220
|
const peerId = peerIdFromPeerDescriptor(contact)
|
|
200
221
|
this.bucket!.remove(peerId.value)
|
|
201
|
-
this.
|
|
222
|
+
this.contacts!.removeContact(peerId)
|
|
202
223
|
this.randomPeers!.removeContact(peerId)
|
|
203
224
|
}
|
|
204
225
|
|
|
@@ -209,46 +230,99 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
|
|
|
209
230
|
this.bucket!.remove(rpcRemote.id)
|
|
210
231
|
})
|
|
211
232
|
this.bucket!.removeAllListeners()
|
|
212
|
-
this.
|
|
233
|
+
this.contacts!.stop()
|
|
213
234
|
this.randomPeers!.stop()
|
|
214
235
|
this.connections.clear()
|
|
215
236
|
}
|
|
216
237
|
|
|
238
|
+
getClosestNeighborsTo(kademliaId: Uint8Array, limit?: number, excludeSet?: Set<PeerIDKey>): DhtNodeRpcRemote[] {
|
|
239
|
+
const closest = new SortedContactList<DhtNodeRpcRemote>({
|
|
240
|
+
referenceId: PeerID.fromValue(kademliaId),
|
|
241
|
+
allowToContainReferenceId: true,
|
|
242
|
+
emitEvents: false
|
|
243
|
+
})
|
|
244
|
+
this.bucket!.toArray().map((contact) => closest.addContact(contact))
|
|
245
|
+
// TODO should set the excludeSet and limit to SortedContactList constructor and remove these line
|
|
246
|
+
return closest.getClosestContacts(limit).filter((contact) => {
|
|
247
|
+
if (!excludeSet) {
|
|
248
|
+
return true
|
|
249
|
+
} else {
|
|
250
|
+
return !excludeSet.has(contact.getPeerId().toKey())
|
|
251
|
+
}
|
|
252
|
+
})
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// TODO reduce copy-paste?
|
|
256
|
+
getClosestContactsTo(kademliaId: Uint8Array, limit?: number, excludeSet?: Set<PeerIDKey>): DhtNodeRpcRemote[] {
|
|
257
|
+
const closest = new SortedContactList<DhtNodeRpcRemote>({
|
|
258
|
+
referenceId: PeerID.fromValue(kademliaId),
|
|
259
|
+
allowToContainReferenceId: true,
|
|
260
|
+
emitEvents: false
|
|
261
|
+
})
|
|
262
|
+
this.contacts!.getAllContacts().map((contact) => closest.addContact(contact))
|
|
263
|
+
// TODO should set the excludeSet and limit to SortedContactList constructor and remove these line
|
|
264
|
+
return closest.getClosestContacts(limit).filter((contact) => {
|
|
265
|
+
if (!excludeSet) {
|
|
266
|
+
return true
|
|
267
|
+
} else {
|
|
268
|
+
return !excludeSet.has(contact.getPeerId().toKey())
|
|
269
|
+
}
|
|
270
|
+
})
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
getNumberOfContacts(excludeSet?: Set<PeerIDKey>): number {
|
|
274
|
+
return this.contacts!.getAllContacts().filter((contact) => {
|
|
275
|
+
if (!excludeSet) {
|
|
276
|
+
return true
|
|
277
|
+
} else {
|
|
278
|
+
return !excludeSet.has(contact.getPeerId().toKey())
|
|
279
|
+
}
|
|
280
|
+
}).length
|
|
281
|
+
}
|
|
282
|
+
|
|
217
283
|
getNumberOfConnections(): number {
|
|
218
284
|
return this.connections.size
|
|
219
285
|
}
|
|
220
286
|
|
|
287
|
+
getNumberOfNeighbors(): number {
|
|
288
|
+
return this.bucket!.count()
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
getNeighbors(): PeerDescriptor[] {
|
|
292
|
+
return this.bucket!.toArray().map((rpcRemote: DhtNodeRpcRemote) => rpcRemote.getPeerDescriptor())
|
|
293
|
+
}
|
|
294
|
+
|
|
221
295
|
handlePeerActive(peerId: PeerID): void {
|
|
222
|
-
this.
|
|
296
|
+
this.contacts!.setActive(peerId)
|
|
223
297
|
}
|
|
224
298
|
|
|
225
299
|
handlePeerUnresponsive(peerId: PeerID): void {
|
|
226
300
|
this.bucket!.remove(peerId.value)
|
|
227
|
-
this.
|
|
301
|
+
this.contacts!.removeContact(peerId)
|
|
228
302
|
}
|
|
229
303
|
|
|
230
|
-
handleNewPeers(
|
|
304
|
+
handleNewPeers(peerDescriptors: PeerDescriptor[], setActive?: boolean): void {
|
|
231
305
|
if (this.stopped) {
|
|
232
306
|
return
|
|
233
307
|
}
|
|
234
|
-
|
|
235
|
-
|
|
308
|
+
peerDescriptors.forEach((contact) => {
|
|
309
|
+
const peerId = peerIdFromPeerDescriptor(contact)
|
|
310
|
+
if (!peerId.equals(this.config.ownPeerId)) {
|
|
236
311
|
logger.trace(`Adding new contact ${getNodeIdFromPeerDescriptor(contact)}`)
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
) {
|
|
241
|
-
this.
|
|
242
|
-
if (setActive) {
|
|
243
|
-
const peerId = peerIdFromPeerDescriptor(contact)
|
|
244
|
-
this.neighborList!.setActive(peerId)
|
|
245
|
-
}
|
|
246
|
-
this.bucket!.add(rpcRemote)
|
|
247
|
-
} else {
|
|
248
|
-
this.randomPeers!.addContact(rpcRemote)
|
|
312
|
+
const remote = this.config.createDhtNodeRpcRemote(contact)
|
|
313
|
+
const isInBucket = (this.bucket!.get(contact.nodeId) !== null)
|
|
314
|
+
const isInNeighborList = (this.contacts!.getContact(peerId) !== undefined)
|
|
315
|
+
if (isInBucket || isInNeighborList) {
|
|
316
|
+
this.randomPeers!.addContact(remote)
|
|
249
317
|
}
|
|
250
|
-
if (
|
|
251
|
-
this.
|
|
318
|
+
if (!isInBucket) {
|
|
319
|
+
this.bucket!.add(remote)
|
|
320
|
+
}
|
|
321
|
+
if (!isInNeighborList) {
|
|
322
|
+
this.contacts!.addContact(remote)
|
|
323
|
+
}
|
|
324
|
+
if (setActive) {
|
|
325
|
+
this.contacts!.setActive(peerId)
|
|
252
326
|
}
|
|
253
327
|
}
|
|
254
328
|
})
|
|
@@ -1,26 +1,34 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { PeerID, PeerIDKey } from '../../helpers/PeerID'
|
|
2
|
+
import { ContactState, Events } from './ContactList'
|
|
3
|
+
import { sortedIndexBy } from 'lodash'
|
|
4
|
+
import EventEmitter from 'eventemitter3'
|
|
5
|
+
import { getDistance } from '../PeerManager'
|
|
6
|
+
|
|
7
|
+
export interface SortedContactListConfig {
|
|
8
|
+
referenceId: PeerID // all contacts in this list are in sorted by the distance to this ID
|
|
9
|
+
allowToContainReferenceId: boolean
|
|
10
|
+
// TODO could maybe optimize this by removing the flag and then we'd check whether we have
|
|
11
|
+
// any listeners before we emit the event
|
|
12
|
+
emitEvents: boolean
|
|
13
|
+
maxSize?: number
|
|
14
|
+
// if set, the list can't contain any contacts which are futher away than this limit
|
|
15
|
+
peerIdDistanceLimit?: PeerID
|
|
16
|
+
// if set, the list can't contain contacts with these ids
|
|
17
|
+
excludedPeerIDs?: PeerID[]
|
|
18
|
+
}
|
|
4
19
|
|
|
5
|
-
export class SortedContactList<C extends { getPeerId: () => PeerID }> extends
|
|
20
|
+
export class SortedContactList<C extends { getPeerId: () => PeerID }> extends EventEmitter<Events<C>> {
|
|
6
21
|
|
|
7
|
-
private
|
|
8
|
-
private
|
|
9
|
-
private
|
|
22
|
+
private config: SortedContactListConfig
|
|
23
|
+
private contactsById: Map<PeerIDKey, ContactState<C>> = new Map()
|
|
24
|
+
private contactIds: PeerID[] = []
|
|
10
25
|
|
|
11
26
|
constructor(
|
|
12
|
-
|
|
13
|
-
maxSize: number,
|
|
14
|
-
defaultContactQueryLimit?: number,
|
|
15
|
-
allowLocalPeerId = false,
|
|
16
|
-
peerIdDistanceLimit?: PeerID,
|
|
17
|
-
excludedPeerIDs?: PeerID[]
|
|
27
|
+
config: SortedContactListConfig
|
|
18
28
|
) {
|
|
19
|
-
super(
|
|
29
|
+
super()
|
|
30
|
+
this.config = config
|
|
20
31
|
this.compareIds = this.compareIds.bind(this)
|
|
21
|
-
this.allowLocalPeerId = allowLocalPeerId
|
|
22
|
-
this.peerIdDistanceLimit = peerIdDistanceLimit
|
|
23
|
-
this.excludedPeerIDs = excludedPeerIDs
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
public getClosestContactId(): PeerID {
|
|
@@ -32,38 +40,44 @@ export class SortedContactList<C extends { getPeerId: () => PeerID }> extends Co
|
|
|
32
40
|
}
|
|
33
41
|
|
|
34
42
|
public addContact(contact: C): void {
|
|
35
|
-
if (this.excludedPeerIDs
|
|
36
|
-
&& this.excludedPeerIDs.some((peerId) => contact.getPeerId().equals(peerId))) {
|
|
43
|
+
if (this.config.excludedPeerIDs !== undefined
|
|
44
|
+
&& this.config.excludedPeerIDs.some((peerId) => contact.getPeerId().equals(peerId))) {
|
|
37
45
|
return
|
|
38
46
|
}
|
|
39
|
-
|
|
40
|
-
if ((!this.
|
|
41
|
-
(this.peerIdDistanceLimit !== undefined && this.compareIds(this.peerIdDistanceLimit, contact.getPeerId()) < 0)) {
|
|
47
|
+
|
|
48
|
+
if ((!this.config.allowToContainReferenceId && this.config.referenceId.equals(contact.getPeerId())) ||
|
|
49
|
+
(this.config.peerIdDistanceLimit !== undefined && this.compareIds(this.config.peerIdDistanceLimit, contact.getPeerId()) < 0)) {
|
|
42
50
|
return
|
|
43
51
|
}
|
|
44
52
|
if (!this.contactsById.has(contact.getPeerId().toKey())) {
|
|
45
|
-
if (this.contactIds.length < this.maxSize) {
|
|
53
|
+
if ((this.config.maxSize === undefined) || (this.contactIds.length < this.config.maxSize)) {
|
|
46
54
|
this.contactsById.set(contact.getPeerId().toKey(), new ContactState(contact))
|
|
47
|
-
|
|
48
|
-
this.contactIds.
|
|
49
|
-
|
|
55
|
+
|
|
56
|
+
const index = sortedIndexBy(this.contactIds, contact.getPeerId(), (id: PeerID) => { return this.distanceToReferenceId(id) })
|
|
57
|
+
this.contactIds.splice(index, 0, contact.getPeerId())
|
|
58
|
+
} else if (this.compareIds(this.contactIds[this.config.maxSize - 1], contact.getPeerId()) > 0) {
|
|
50
59
|
const removedId = this.contactIds.pop()
|
|
51
60
|
const removedContact = this.contactsById.get(removedId!.toKey())!.contact
|
|
52
61
|
this.contactsById.delete(removedId!.toKey())
|
|
53
62
|
this.contactsById.set(contact.getPeerId().toKey(), new ContactState(contact))
|
|
54
|
-
|
|
55
|
-
this.contactIds.
|
|
63
|
+
|
|
64
|
+
const index = sortedIndexBy(this.contactIds, contact.getPeerId(), (id: PeerID) => { return this.distanceToReferenceId(id) })
|
|
65
|
+
this.contactIds.splice(index, 0, contact.getPeerId())
|
|
66
|
+
if (this.config.emitEvents) {
|
|
67
|
+
this.emit(
|
|
68
|
+
'contactRemoved',
|
|
69
|
+
removedContact,
|
|
70
|
+
this.getClosestContacts()
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (this.config.emitEvents) {
|
|
56
75
|
this.emit(
|
|
57
|
-
'
|
|
58
|
-
|
|
76
|
+
'newContact',
|
|
77
|
+
contact,
|
|
59
78
|
this.getClosestContacts()
|
|
60
79
|
)
|
|
61
80
|
}
|
|
62
|
-
this.emit(
|
|
63
|
-
'newContact',
|
|
64
|
-
contact,
|
|
65
|
-
this.getClosestContacts()
|
|
66
|
-
)
|
|
67
81
|
}
|
|
68
82
|
}
|
|
69
83
|
|
|
@@ -71,6 +85,10 @@ export class SortedContactList<C extends { getPeerId: () => PeerID }> extends Co
|
|
|
71
85
|
contacts.forEach((contact) => this.addContact(contact))
|
|
72
86
|
}
|
|
73
87
|
|
|
88
|
+
public getContact(id: PeerID): ContactState<C> | undefined {
|
|
89
|
+
return this.contactsById.get(id.toKey())
|
|
90
|
+
}
|
|
91
|
+
|
|
74
92
|
public setContacted(contactId: PeerID): void {
|
|
75
93
|
if (this.contactsById.has(contactId.toKey())) {
|
|
76
94
|
this.contactsById.get(contactId.toKey())!.contacted = true
|
|
@@ -83,7 +101,7 @@ export class SortedContactList<C extends { getPeerId: () => PeerID }> extends Co
|
|
|
83
101
|
}
|
|
84
102
|
}
|
|
85
103
|
|
|
86
|
-
public getClosestContacts(limit
|
|
104
|
+
public getClosestContacts(limit?: number): C[] {
|
|
87
105
|
const ret: C[] = []
|
|
88
106
|
this.contactIds.forEach((contactId) => {
|
|
89
107
|
const contact = this.contactsById.get(contactId.toKey())
|
|
@@ -91,7 +109,11 @@ export class SortedContactList<C extends { getPeerId: () => PeerID }> extends Co
|
|
|
91
109
|
ret.push(contact.contact)
|
|
92
110
|
}
|
|
93
111
|
})
|
|
94
|
-
|
|
112
|
+
if (limit === undefined) {
|
|
113
|
+
return ret
|
|
114
|
+
} else {
|
|
115
|
+
return ret.slice(0, limit)
|
|
116
|
+
}
|
|
95
117
|
}
|
|
96
118
|
|
|
97
119
|
public getUncontactedContacts(num: number): C[] {
|
|
@@ -124,22 +146,29 @@ export class SortedContactList<C extends { getPeerId: () => PeerID }> extends Co
|
|
|
124
146
|
}
|
|
125
147
|
|
|
126
148
|
public compareIds(id1: PeerID, id2: PeerID): number {
|
|
127
|
-
const distance1 =
|
|
128
|
-
const distance2 =
|
|
149
|
+
const distance1 = this.distanceToReferenceId(id1)
|
|
150
|
+
const distance2 = this.distanceToReferenceId(id2)
|
|
129
151
|
return distance1 - distance2
|
|
130
152
|
}
|
|
131
153
|
|
|
154
|
+
// TODO inline this method?
|
|
155
|
+
private distanceToReferenceId(id: PeerID): number {
|
|
156
|
+
return getDistance(this.config.referenceId.value, id.value)
|
|
157
|
+
}
|
|
158
|
+
|
|
132
159
|
public removeContact(id: PeerID): boolean {
|
|
133
160
|
if (this.contactsById.has(id.toKey())) {
|
|
134
161
|
const removed = this.contactsById.get(id.toKey())!.contact
|
|
135
162
|
const index = this.contactIds.findIndex((element) => element.equals(id))
|
|
136
163
|
this.contactIds.splice(index, 1)
|
|
137
164
|
this.contactsById.delete(id.toKey())
|
|
138
|
-
this.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
165
|
+
if (this.config.emitEvents) {
|
|
166
|
+
this.emit(
|
|
167
|
+
'contactRemoved',
|
|
168
|
+
removed,
|
|
169
|
+
this.getClosestContacts()
|
|
170
|
+
)
|
|
171
|
+
}
|
|
143
172
|
return true
|
|
144
173
|
}
|
|
145
174
|
return false
|
|
@@ -152,4 +181,18 @@ export class SortedContactList<C extends { getPeerId: () => PeerID }> extends Co
|
|
|
152
181
|
public getAllContacts(): C[] {
|
|
153
182
|
return this.contactIds.map((peerId) => this.contactsById.get(peerId.toKey())!.contact)
|
|
154
183
|
}
|
|
184
|
+
|
|
185
|
+
public getSize(): number {
|
|
186
|
+
return this.contactIds.length
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
public clear(): void {
|
|
190
|
+
this.contactsById.clear()
|
|
191
|
+
this.contactIds = []
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
public stop(): void {
|
|
195
|
+
this.removeAllListeners()
|
|
196
|
+
this.clear()
|
|
197
|
+
}
|
|
155
198
|
}
|
|
@@ -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,7 +15,6 @@ 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
|
|
@@ -31,6 +29,7 @@ export class DiscoverySession {
|
|
|
31
29
|
private noProgressCounter = 0
|
|
32
30
|
private ongoingClosestPeersRequests: Set<string> = new Set()
|
|
33
31
|
private readonly config: DiscoverySessionConfig
|
|
32
|
+
private contactedPeers: Set<PeerIDKey> = new Set()
|
|
34
33
|
|
|
35
34
|
constructor(config: DiscoverySessionConfig) {
|
|
36
35
|
this.config = config
|
|
@@ -49,7 +48,7 @@ export class DiscoverySession {
|
|
|
49
48
|
}
|
|
50
49
|
logger.trace(`Getting closest peers from contact: ${getNodeIdFromPeerDescriptor(contact.getPeerDescriptor())}`)
|
|
51
50
|
this.outgoingClosestPeersRequestsCounter++
|
|
52
|
-
this.
|
|
51
|
+
this.contactedPeers.add(contact.getPeerId().toKey())
|
|
53
52
|
const returnedContacts = await contact.getClosestPeers(this.config.targetId)
|
|
54
53
|
this.config.peerManager.handlePeerActive(contact.getPeerId())
|
|
55
54
|
return returnedContacts
|
|
@@ -60,9 +59,12 @@ export class DiscoverySession {
|
|
|
60
59
|
return
|
|
61
60
|
}
|
|
62
61
|
this.ongoingClosestPeersRequests.delete(peerId.toKey())
|
|
63
|
-
const
|
|
62
|
+
const oldClosestNeighbor = this.config.peerManager.getClosestNeighborsTo(this.config.targetId, 1)[0]
|
|
63
|
+
const oldClosestDistance = getDistance(this.config.targetId, oldClosestNeighbor.getPeerId().value)
|
|
64
64
|
this.addNewContacts(contacts)
|
|
65
|
-
|
|
65
|
+
const newClosestNeighbor = this.config.peerManager.getClosestNeighborsTo(this.config.targetId, 1)[0]
|
|
66
|
+
const newClosestDistance = getDistance(this.config.targetId, newClosestNeighbor.getPeerId().value)
|
|
67
|
+
if (newClosestDistance >= oldClosestDistance) {
|
|
66
68
|
this.noProgressCounter++
|
|
67
69
|
} else {
|
|
68
70
|
this.noProgressCounter = 0
|
|
@@ -81,7 +83,7 @@ export class DiscoverySession {
|
|
|
81
83
|
if (this.stopped) {
|
|
82
84
|
return
|
|
83
85
|
}
|
|
84
|
-
const uncontacted = this.config.peerManager.
|
|
86
|
+
const uncontacted = this.config.peerManager.getClosestContactsTo(this.config.targetId, this.config.parallelism, this.contactedPeers)
|
|
85
87
|
if (uncontacted.length === 0 || this.noProgressCounter >= this.config.noProgressLimit) {
|
|
86
88
|
this.emitter.emit('discoveryCompleted')
|
|
87
89
|
this.stopped = true
|
|
@@ -103,18 +105,16 @@ export class DiscoverySession {
|
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
|
|
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!
|
|
108
|
+
public async findClosestNodes(timeout: number): Promise<void> {
|
|
109
|
+
if (this.config.peerManager.getNumberOfContacts(this.contactedPeers) === 0) {
|
|
110
|
+
return
|
|
111
111
|
}
|
|
112
|
+
// TODO add abortController and signal it in stop()
|
|
112
113
|
await runAndWaitForEvents3<DiscoverySessionEvents>(
|
|
113
114
|
[this.findMoreContacts.bind(this)],
|
|
114
115
|
[[this.emitter, 'discoveryCompleted']],
|
|
115
116
|
timeout
|
|
116
117
|
)
|
|
117
|
-
return this.config.peerManager.neighborList!
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
public stop(): void {
|
|
@@ -4,9 +4,9 @@ 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 { PeerManager } from '../PeerManager'
|
|
10
10
|
|
|
11
11
|
interface PeerDiscoveryConfig {
|
|
12
12
|
localPeerDescriptor: PeerDescriptor
|
|
@@ -51,8 +51,6 @@ export class PeerDiscovery {
|
|
|
51
51
|
this.config.connectionManager?.lockConnection(entryPointDescriptor, `${this.config.serviceId}::joinDht`)
|
|
52
52
|
this.config.peerManager.handleNewPeers([entryPointDescriptor])
|
|
53
53
|
const targetId = peerIdFromPeerDescriptor(this.config.localPeerDescriptor).value
|
|
54
|
-
const closest = this.config.peerManager.bucket!.closest(targetId, this.config.peerDiscoveryQueryBatchSize)
|
|
55
|
-
this.config.peerManager.neighborList!.addContacts(closest)
|
|
56
54
|
const sessions = [this.createSession(targetId)]
|
|
57
55
|
if (doAdditionalRandomPeerDiscovery) {
|
|
58
56
|
sessions.push(this.createSession(createRandomNodeId()))
|
|
@@ -65,7 +63,6 @@ export class PeerDiscovery {
|
|
|
65
63
|
private createSession(targetId: Uint8Array): DiscoverySession {
|
|
66
64
|
const sessionOptions = {
|
|
67
65
|
targetId,
|
|
68
|
-
localPeerDescriptor: this.config.localPeerDescriptor,
|
|
69
66
|
parallelism: this.config.parallelism,
|
|
70
67
|
noProgressLimit: this.config.joinNoProgressLimit,
|
|
71
68
|
peerManager: this.config.peerManager
|
|
@@ -83,7 +80,7 @@ export class PeerDiscovery {
|
|
|
83
80
|
logger.debug(`DHT join on ${this.config.serviceId} timed out`)
|
|
84
81
|
} finally {
|
|
85
82
|
if (!this.isStopped()) {
|
|
86
|
-
if (this.config.peerManager.
|
|
83
|
+
if (this.config.peerManager.getNumberOfNeighbors() === 0) {
|
|
87
84
|
if (retry) {
|
|
88
85
|
// TODO should we catch possible promise rejection?
|
|
89
86
|
setAbortableTimeout(() => this.rejoinDht(entryPointDescriptor), 1000, this.abortController.signal)
|
|
@@ -103,7 +100,6 @@ export class PeerDiscovery {
|
|
|
103
100
|
logger.debug(`Rejoining DHT ${this.config.serviceId}`)
|
|
104
101
|
this.rejoinOngoing = true
|
|
105
102
|
try {
|
|
106
|
-
this.config.peerManager.neighborList!.clear()
|
|
107
103
|
await this.joinDht(entryPoint)
|
|
108
104
|
logger.debug(`Rejoined DHT successfully ${this.config.serviceId}!`)
|
|
109
105
|
} catch (err) {
|
|
@@ -128,16 +124,13 @@ export class PeerDiscovery {
|
|
|
128
124
|
if (this.isStopped()) {
|
|
129
125
|
return
|
|
130
126
|
}
|
|
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])
|
|
127
|
+
const nodes = this.config.peerManager.getClosestNeighborsTo(this.config.localPeerDescriptor.nodeId, this.config.parallelism)
|
|
128
|
+
await Promise.allSettled(
|
|
129
|
+
nodes.map(async (peer: DhtNodeRpcRemote) => {
|
|
130
|
+
const contacts = await peer.getClosestPeers(this.config.localPeerDescriptor.nodeId!)
|
|
131
|
+
this.config.peerManager.handleNewPeers(contacts)
|
|
139
132
|
})
|
|
140
|
-
|
|
133
|
+
)
|
|
141
134
|
}
|
|
142
135
|
|
|
143
136
|
public isJoinOngoing(): boolean {
|
|
@@ -158,7 +151,7 @@ export class PeerDiscovery {
|
|
|
158
151
|
clearTimeout(this.rejoinTimeoutRef)
|
|
159
152
|
this.rejoinTimeoutRef = undefined
|
|
160
153
|
}
|
|
161
|
-
this.ongoingDiscoverySessions.forEach((session
|
|
154
|
+
this.ongoingDiscoverySessions.forEach((session) => {
|
|
162
155
|
session.stop()
|
|
163
156
|
})
|
|
164
157
|
}
|
|
@@ -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
|