@streamr/dht 100.0.0-testnet-three.1 → 100.0.0-testnet-three.3
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/dist/src/connection/connectivityRequestHandler.js +25 -21
- package/dist/src/connection/connectivityRequestHandler.js.map +1 -1
- package/dist/src/connection/websocket/WebsocketConnector.js +10 -1
- package/dist/src/connection/websocket/WebsocketConnector.js.map +1 -1
- package/dist/src/dht/DhtNode.d.ts +3 -2
- package/dist/src/dht/DhtNode.js +28 -11
- package/dist/src/dht/DhtNode.js.map +1 -1
- package/dist/src/dht/DhtNodeRpcLocal.d.ts +1 -1
- package/dist/src/dht/DhtNodeRpcLocal.js +2 -2
- package/dist/src/dht/DhtNodeRpcLocal.js.map +1 -1
- package/dist/src/dht/PeerManager.d.ts +7 -9
- package/dist/src/dht/PeerManager.js +7 -14
- package/dist/src/dht/PeerManager.js.map +1 -1
- package/dist/src/dht/contact/ContactList.d.ts +1 -1
- package/dist/src/dht/contact/RandomContactList.js +1 -1
- package/dist/src/dht/contact/RandomContactList.js.map +1 -1
- package/dist/src/dht/contact/SortedContactList.js +18 -17
- package/dist/src/dht/contact/SortedContactList.js.map +1 -1
- package/dist/src/dht/discovery/DiscoverySession.d.ts +1 -1
- package/dist/src/dht/discovery/DiscoverySession.js +5 -5
- package/dist/src/dht/discovery/DiscoverySession.js.map +1 -1
- package/dist/src/dht/discovery/PeerDiscovery.js +2 -2
- package/dist/src/dht/discovery/PeerDiscovery.js.map +1 -1
- package/dist/src/dht/recursive-operation/RecursiveOperationRpcLocal.js +0 -1
- package/dist/src/dht/recursive-operation/RecursiveOperationRpcLocal.js.map +1 -1
- package/dist/src/dht/routing/RouterRpcLocal.js +2 -3
- package/dist/src/dht/routing/RouterRpcLocal.js.map +1 -1
- package/dist/src/dht/routing/RoutingSession.js +2 -1
- package/dist/src/dht/routing/RoutingSession.js.map +1 -1
- package/dist/src/dht/store/LocalDataStore.d.ts +1 -1
- package/dist/src/dht/store/LocalDataStore.js +2 -5
- package/dist/src/dht/store/LocalDataStore.js.map +1 -1
- package/dist/src/dht/store/StoreManager.d.ts +1 -1
- package/dist/src/dht/store/StoreManager.js +10 -17
- package/dist/src/dht/store/StoreManager.js.map +1 -1
- package/dist/src/identifiers.d.ts +1 -0
- package/dist/src/identifiers.js +3 -3
- package/dist/src/identifiers.js.map +1 -1
- package/package.json +5 -5
- package/src/connection/connectivityRequestHandler.ts +26 -22
- package/src/connection/websocket/WebsocketConnector.ts +9 -1
- package/src/dht/DhtNode.ts +33 -16
- package/src/dht/DhtNodeRpcLocal.ts +3 -3
- package/src/dht/PeerManager.ts +12 -21
- package/src/dht/contact/ContactList.ts +1 -1
- package/src/dht/contact/RandomContactList.ts +1 -1
- package/src/dht/contact/SortedContactList.ts +26 -24
- package/src/dht/discovery/DiscoverySession.ts +5 -5
- package/src/dht/discovery/PeerDiscovery.ts +2 -2
- package/src/dht/recursive-operation/RecursiveOperationRpcLocal.ts +0 -1
- package/src/dht/routing/RouterRpcLocal.ts +2 -3
- package/src/dht/routing/RoutingSession.ts +2 -1
- package/src/dht/store/LocalDataStore.ts +2 -5
- package/src/dht/store/StoreManager.ts +10 -17
- package/src/identifiers.ts +1 -1
- package/test/end-to-end/Layer0Webrtc-Layer1.test.ts +5 -5
- package/test/end-to-end/Layer0Webrtc.test.ts +0 -2
- package/test/unit/PeerManager.test.ts +1 -1
- package/test/unit/SortedContactList.test.ts +13 -0
- package/test/unit/StoreManager.test.ts +26 -23
|
@@ -34,10 +34,25 @@ export const attachConnectivityRequestHandler = (connectionToListenTo: ServerWeb
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
const handleIncomingConnectivityRequest = async (connection: ServerWebsocket, connectivityRequest: ConnectivityRequest): Promise<void> => {
|
|
37
|
-
let outgoingConnection: IConnection | undefined
|
|
38
|
-
let connectivityResponseMessage: ConnectivityResponse | undefined
|
|
39
37
|
const host = connectivityRequest.host ?? connection.getRemoteAddress()
|
|
40
38
|
const ipAddress = connection.getRemoteIp()
|
|
39
|
+
const connectivityResponse = await connectivityProbe(connectivityRequest, ipAddress, host)
|
|
40
|
+
const msg: Message = {
|
|
41
|
+
serviceId: CONNECTIVITY_CHECKER_SERVICE_ID,
|
|
42
|
+
messageType: MessageType.CONNECTIVITY_RESPONSE,
|
|
43
|
+
messageId: v4(),
|
|
44
|
+
body: {
|
|
45
|
+
oneofKind: 'connectivityResponse',
|
|
46
|
+
connectivityResponse
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
connection.send(Message.toBinary(msg))
|
|
50
|
+
logger.trace('ConnectivityResponse sent: ' + JSON.stringify(Message.toJson(msg)))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const connectivityProbe = async (connectivityRequest: ConnectivityRequest, ipAddress: string, host: string): Promise<ConnectivityResponse> => {
|
|
54
|
+
let outgoingConnection: IConnection | undefined
|
|
55
|
+
let connectivityResponseMessage: ConnectivityResponse
|
|
41
56
|
try {
|
|
42
57
|
const wsServerInfo = {
|
|
43
58
|
host,
|
|
@@ -50,6 +65,14 @@ const handleIncomingConnectivityRequest = async (connection: ServerWebsocket, co
|
|
|
50
65
|
url,
|
|
51
66
|
selfSigned: connectivityRequest.selfSigned
|
|
52
67
|
})
|
|
68
|
+
logger.trace('Connectivity test produced positive result, communicating reply to the requester ' + host + ':' + connectivityRequest.port)
|
|
69
|
+
connectivityResponseMessage = {
|
|
70
|
+
host,
|
|
71
|
+
natType: NatType.OPEN_INTERNET,
|
|
72
|
+
websocket: { host, port: connectivityRequest.port, tls: connectivityRequest.tls },
|
|
73
|
+
ipAddress: ipv4ToNumber(ipAddress),
|
|
74
|
+
version: localVersion
|
|
75
|
+
}
|
|
53
76
|
} catch (err) {
|
|
54
77
|
logger.debug('error', { err })
|
|
55
78
|
connectivityResponseMessage = {
|
|
@@ -62,25 +85,6 @@ const handleIncomingConnectivityRequest = async (connection: ServerWebsocket, co
|
|
|
62
85
|
if (outgoingConnection) {
|
|
63
86
|
// TODO should we have some handling for this floating promise?
|
|
64
87
|
outgoingConnection.close(false)
|
|
65
|
-
logger.trace('Connectivity test produced positive result, communicating reply to the requester ' + host + ':' + connectivityRequest.port)
|
|
66
|
-
|
|
67
|
-
connectivityResponseMessage = {
|
|
68
|
-
host,
|
|
69
|
-
natType: NatType.OPEN_INTERNET,
|
|
70
|
-
websocket: { host, port: connectivityRequest.port, tls: connectivityRequest.tls },
|
|
71
|
-
ipAddress: ipv4ToNumber(ipAddress),
|
|
72
|
-
version: localVersion
|
|
73
|
-
}
|
|
74
88
|
}
|
|
75
|
-
|
|
76
|
-
serviceId: CONNECTIVITY_CHECKER_SERVICE_ID,
|
|
77
|
-
messageType: MessageType.CONNECTIVITY_RESPONSE,
|
|
78
|
-
messageId: v4(),
|
|
79
|
-
body: {
|
|
80
|
-
oneofKind: 'connectivityResponse',
|
|
81
|
-
connectivityResponse: connectivityResponseMessage!
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
connection.send(Message.toBinary(msg))
|
|
85
|
-
logger.trace('ConnectivityResponse sent: ' + JSON.stringify(Message.toJson(msg)))
|
|
89
|
+
return connectivityResponseMessage
|
|
86
90
|
}
|
|
@@ -153,7 +153,15 @@ export class WebsocketConnector {
|
|
|
153
153
|
} else if (action === 'connectivityProbe') {
|
|
154
154
|
// no-op
|
|
155
155
|
} else {
|
|
156
|
-
|
|
156
|
+
// The localPeerDescriptor can be undefined here as the WS server is used for connectivity checks
|
|
157
|
+
// before the localPeerDescriptor is set during start.
|
|
158
|
+
// Handshaked connections should be rejected before the localPeerDescriptor is set.
|
|
159
|
+
if (this.localPeerDescriptor !== undefined) {
|
|
160
|
+
this.attachHandshaker(connection)
|
|
161
|
+
} else {
|
|
162
|
+
logger.trace('incoming Websocket connection before localPeerDescriptor was set, closing connection')
|
|
163
|
+
connection.close(false).catch(() => {})
|
|
164
|
+
}
|
|
157
165
|
}
|
|
158
166
|
})
|
|
159
167
|
const port = await this.websocketServer.start()
|
package/src/dht/DhtNode.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { ConnectionManager, PortRange, TlsCertificate } from '../connection/Conn
|
|
|
12
12
|
import { DefaultConnectorFacade, DefaultConnectorFacadeConfig } from '../connection/ConnectorFacade'
|
|
13
13
|
import { IceServer } from '../connection/webrtc/WebrtcConnector'
|
|
14
14
|
import { isBrowserEnvironment } from '../helpers/browser/isBrowserEnvironment'
|
|
15
|
-
import { DhtAddress, getNodeIdFromPeerDescriptor } from '../identifiers'
|
|
15
|
+
import { DhtAddress, KADEMLIA_ID_LENGTH_IN_BYTES, getNodeIdFromPeerDescriptor } from '../identifiers'
|
|
16
16
|
import { Any } from '../proto/google/protobuf/any'
|
|
17
17
|
import {
|
|
18
18
|
ClosestPeersRequest,
|
|
@@ -48,9 +48,9 @@ import { StoreRpcRemote } from './store/StoreRpcRemote'
|
|
|
48
48
|
import { createPeerDescriptor } from '../helpers/createPeerDescriptor'
|
|
49
49
|
|
|
50
50
|
export interface DhtNodeEvents {
|
|
51
|
-
|
|
51
|
+
contactAdded: (peerDescriptor: PeerDescriptor, closestPeers: PeerDescriptor[]) => void
|
|
52
52
|
contactRemoved: (peerDescriptor: PeerDescriptor, closestPeers: PeerDescriptor[]) => void
|
|
53
|
-
|
|
53
|
+
randomContactAdded: (peerDescriptor: PeerDescriptor, closestPeers: PeerDescriptor[]) => void
|
|
54
54
|
randomContactRemoved: (peerDescriptor: PeerDescriptor, closestPeers: PeerDescriptor[]) => void
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -143,10 +143,27 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
143
143
|
storageRedundancyFactor: 5,
|
|
144
144
|
metricsContext: new MetricsContext()
|
|
145
145
|
}, conf)
|
|
146
|
+
this.validateConfig()
|
|
146
147
|
this.localDataStore = new LocalDataStore(this.config.storeMaxTtl)
|
|
147
148
|
this.send = this.send.bind(this)
|
|
148
149
|
}
|
|
149
150
|
|
|
151
|
+
private validateConfig(): void {
|
|
152
|
+
const expectedNodeIdLength = KADEMLIA_ID_LENGTH_IN_BYTES * 2
|
|
153
|
+
if (this.config.nodeId !== undefined ) {
|
|
154
|
+
if (!/^[0-9a-fA-F]+$/.test(this.config.nodeId)) {
|
|
155
|
+
throw new Error('Invalid nodeId, the nodeId should be a hex string')
|
|
156
|
+
} else if (this.config.nodeId.length !== expectedNodeIdLength) {
|
|
157
|
+
throw new Error(`Invalid nodeId, the length of the nodeId should be ${expectedNodeIdLength}`)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (this.config.peerDescriptor !== undefined) {
|
|
161
|
+
if (this.config.peerDescriptor.nodeId.length !== KADEMLIA_ID_LENGTH_IN_BYTES) {
|
|
162
|
+
throw new Error(`Invalid peerDescriptor, the length of the nodeId should be ${KADEMLIA_ID_LENGTH_IN_BYTES} bytes`)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
150
167
|
public async start(): Promise<void> {
|
|
151
168
|
if (this.started || this.abortController.signal.aborted) {
|
|
152
169
|
return
|
|
@@ -232,7 +249,7 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
232
249
|
rpcCommunicator: this.rpcCommunicator,
|
|
233
250
|
connections: this.peerManager!.connections,
|
|
234
251
|
localPeerDescriptor: this.localPeerDescriptor!,
|
|
235
|
-
addContact: (contact: PeerDescriptor, setActive?: boolean) => this.peerManager!.
|
|
252
|
+
addContact: (contact: PeerDescriptor, setActive?: boolean) => this.peerManager!.addContact([contact], setActive),
|
|
236
253
|
connectionManager: this.connectionManager
|
|
237
254
|
})
|
|
238
255
|
this.recursiveOperationManager = new RecursiveOperationManager({
|
|
@@ -242,7 +259,7 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
242
259
|
connections: this.peerManager!.connections,
|
|
243
260
|
localPeerDescriptor: this.localPeerDescriptor!,
|
|
244
261
|
serviceId: this.config.serviceId,
|
|
245
|
-
addContact: (contact: PeerDescriptor) => this.peerManager!.
|
|
262
|
+
addContact: (contact: PeerDescriptor) => this.peerManager!.addContact([contact]),
|
|
246
263
|
localDataStore: this.localDataStore
|
|
247
264
|
})
|
|
248
265
|
this.storeManager = new StoreManager({
|
|
@@ -266,8 +283,8 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
266
283
|
)
|
|
267
284
|
}
|
|
268
285
|
})
|
|
269
|
-
this.on('
|
|
270
|
-
this.storeManager!.
|
|
286
|
+
this.on('contactAdded', (peerDescriptor: PeerDescriptor) => {
|
|
287
|
+
this.storeManager!.onContactAdded(peerDescriptor)
|
|
271
288
|
})
|
|
272
289
|
this.bindRpcLocalMethods()
|
|
273
290
|
}
|
|
@@ -285,14 +302,14 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
285
302
|
this.peerManager.on('contactRemoved', (peerDescriptor: PeerDescriptor, activeContacts: PeerDescriptor[]) => {
|
|
286
303
|
this.emit('contactRemoved', peerDescriptor, activeContacts)
|
|
287
304
|
})
|
|
288
|
-
this.peerManager.on('
|
|
289
|
-
this.emit('
|
|
305
|
+
this.peerManager.on('contactAdded', (peerDescriptor: PeerDescriptor, activeContacts: PeerDescriptor[]) =>
|
|
306
|
+
this.emit('contactAdded', peerDescriptor, activeContacts)
|
|
290
307
|
)
|
|
291
308
|
this.peerManager.on('randomContactRemoved', (peerDescriptor: PeerDescriptor, activeContacts: PeerDescriptor[]) =>
|
|
292
309
|
this.emit('randomContactRemoved', peerDescriptor, activeContacts)
|
|
293
310
|
)
|
|
294
|
-
this.peerManager.on('
|
|
295
|
-
this.emit('
|
|
311
|
+
this.peerManager.on('randomContactAdded', (peerDescriptor: PeerDescriptor, activeContacts: PeerDescriptor[]) =>
|
|
312
|
+
this.emit('randomContactAdded', peerDescriptor, activeContacts)
|
|
296
313
|
)
|
|
297
314
|
this.peerManager.on('kBucketEmpty', () => {
|
|
298
315
|
if (!this.peerDiscovery!.isJoinOngoing()
|
|
@@ -308,15 +325,15 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
308
325
|
}
|
|
309
326
|
})
|
|
310
327
|
this.transport!.on('connected', (peerDescriptor: PeerDescriptor) => {
|
|
311
|
-
this.peerManager!.
|
|
328
|
+
this.peerManager!.onContactConnected(peerDescriptor)
|
|
312
329
|
this.emit('connected', peerDescriptor)
|
|
313
330
|
})
|
|
314
331
|
this.transport!.on('disconnected', (peerDescriptor: PeerDescriptor, gracefulLeave: boolean) => {
|
|
315
|
-
this.peerManager!.
|
|
332
|
+
this.peerManager!.onContactDisconnected(getNodeIdFromPeerDescriptor(peerDescriptor), gracefulLeave)
|
|
316
333
|
this.emit('disconnected', peerDescriptor, gracefulLeave)
|
|
317
334
|
})
|
|
318
335
|
this.transport!.getConnections().forEach((peer) => {
|
|
319
|
-
this.peerManager!.
|
|
336
|
+
this.peerManager!.onContactConnected(peer)
|
|
320
337
|
})
|
|
321
338
|
}
|
|
322
339
|
|
|
@@ -330,7 +347,7 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
330
347
|
return this.peerManager!.getClosestNeighborsTo(nodeId, limit)
|
|
331
348
|
.map((dhtPeer: DhtNodeRpcRemote) => dhtPeer.getPeerDescriptor())
|
|
332
349
|
},
|
|
333
|
-
|
|
350
|
+
addContact: (contact: PeerDescriptor) => this.peerManager!.addContact([contact]),
|
|
334
351
|
removeContact: (nodeId: DhtAddress) => this.removeContact(nodeId)
|
|
335
352
|
})
|
|
336
353
|
this.rpcCommunicator!.registerRpcMethod(ClosestPeersRequest, ClosestPeersResponse, 'getClosestPeers',
|
|
@@ -400,7 +417,7 @@ export class DhtNode extends EventEmitter<Events> implements ITransport {
|
|
|
400
417
|
if (!this.started) { // the stopped state is checked in PeerManager
|
|
401
418
|
return
|
|
402
419
|
}
|
|
403
|
-
this.peerManager!.
|
|
420
|
+
this.peerManager!.removeContact(nodeId)
|
|
404
421
|
}
|
|
405
422
|
|
|
406
423
|
public async send(msg: Message): Promise<void> {
|
|
@@ -15,7 +15,7 @@ import { DhtAddress, getDhtAddressFromRaw, getNodeIdFromPeerDescriptor } from '.
|
|
|
15
15
|
interface DhtNodeRpcLocalConfig {
|
|
16
16
|
peerDiscoveryQueryBatchSize: number
|
|
17
17
|
getClosestPeersTo: (nodeId: DhtAddress, limit: number) => PeerDescriptor[]
|
|
18
|
-
|
|
18
|
+
addContact: (contact: PeerDescriptor) => void
|
|
19
19
|
removeContact: (nodeId: DhtAddress) => void
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -30,7 +30,7 @@ export class DhtNodeRpcLocal implements IDhtNodeRpc {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
async getClosestPeers(request: ClosestPeersRequest, context: ServerCallContext): Promise<ClosestPeersResponse> {
|
|
33
|
-
this.config.
|
|
33
|
+
this.config.addContact((context as DhtCallContext).incomingSourceDescriptor!)
|
|
34
34
|
const response = {
|
|
35
35
|
peers: this.config.getClosestPeersTo(getDhtAddressFromRaw(request.nodeId), this.config.peerDiscoveryQueryBatchSize),
|
|
36
36
|
requestId: request.requestId
|
|
@@ -41,7 +41,7 @@ export class DhtNodeRpcLocal implements IDhtNodeRpc {
|
|
|
41
41
|
async ping(request: PingRequest, context: ServerCallContext): Promise<PingResponse> {
|
|
42
42
|
logger.trace('received ping request: ' + getNodeIdFromPeerDescriptor((context as DhtCallContext).incomingSourceDescriptor!))
|
|
43
43
|
setImmediate(() => {
|
|
44
|
-
this.config.
|
|
44
|
+
this.config.addContact((context as DhtCallContext).incomingSourceDescriptor!)
|
|
45
45
|
})
|
|
46
46
|
const response: PingResponse = {
|
|
47
47
|
requestId: request.requestId
|
package/src/dht/PeerManager.ts
CHANGED
|
@@ -25,9 +25,9 @@ interface PeerManagerConfig {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export interface PeerManagerEvents {
|
|
28
|
-
|
|
28
|
+
contactAdded: (peerDescriptor: PeerDescriptor, closestPeers: PeerDescriptor[]) => void
|
|
29
29
|
contactRemoved: (peerDescriptor: PeerDescriptor, closestPeers: PeerDescriptor[]) => void
|
|
30
|
-
|
|
30
|
+
randomContactAdded: (peerDescriptor: PeerDescriptor, closestPeers: PeerDescriptor[]) => void
|
|
31
31
|
randomContactRemoved: (peerDescriptor: PeerDescriptor, closestPeers: PeerDescriptor[]) => void
|
|
32
32
|
kBucketEmpty: () => void
|
|
33
33
|
}
|
|
@@ -81,15 +81,15 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
|
|
|
81
81
|
this.emit('contactRemoved', removedContact.getPeerDescriptor(), activeContacts.map((c) => c.getPeerDescriptor()))
|
|
82
82
|
this.randomPeers.addContact(this.config.createDhtNodeRpcRemote(removedContact.getPeerDescriptor()))
|
|
83
83
|
})
|
|
84
|
-
this.contacts.on('
|
|
85
|
-
this.emit('
|
|
84
|
+
this.contacts.on('contactAdded', (contactAdded: DhtNodeRpcRemote, activeContacts: DhtNodeRpcRemote[]) =>
|
|
85
|
+
this.emit('contactAdded', contactAdded.getPeerDescriptor(), activeContacts.map((c) => c.getPeerDescriptor()))
|
|
86
86
|
)
|
|
87
87
|
this.randomPeers = new RandomContactList(this.config.localNodeId, this.config.maxContactListSize)
|
|
88
88
|
this.randomPeers.on('contactRemoved', (removedContact: DhtNodeRpcRemote, activeContacts: DhtNodeRpcRemote[]) =>
|
|
89
89
|
this.emit('randomContactRemoved', removedContact.getPeerDescriptor(), activeContacts.map((c) => c.getPeerDescriptor()))
|
|
90
90
|
)
|
|
91
|
-
this.randomPeers.on('
|
|
92
|
-
this.emit('
|
|
91
|
+
this.randomPeers.on('contactAdded', (contactAdded: DhtNodeRpcRemote, activeContacts: DhtNodeRpcRemote[]) =>
|
|
92
|
+
this.emit('randomContactAdded', contactAdded.getPeerDescriptor(), activeContacts.map((c) => c.getPeerDescriptor()))
|
|
93
93
|
)
|
|
94
94
|
}
|
|
95
95
|
|
|
@@ -160,7 +160,7 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
|
|
|
160
160
|
}
|
|
161
161
|
const closest = this.getClosestActiveContactNotInBucket()
|
|
162
162
|
if (closest) {
|
|
163
|
-
this.
|
|
163
|
+
this.addContact([closest.getPeerDescriptor()])
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
|
|
@@ -173,7 +173,7 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
|
|
|
173
173
|
return undefined
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
|
|
176
|
+
onContactConnected(peerDescriptor: PeerDescriptor): void {
|
|
177
177
|
const nodeId = getNodeIdFromPeerDescriptor(peerDescriptor)
|
|
178
178
|
if (nodeId === this.config.localNodeId) {
|
|
179
179
|
logger.error('handleConnected() to self')
|
|
@@ -188,7 +188,7 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
|
|
|
188
188
|
logger.trace('connected: ' + nodeId + ' ' + this.connections.size)
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
|
|
191
|
+
onContactDisconnected(nodeId: DhtAddress, gracefulLeave: boolean): void {
|
|
192
192
|
logger.trace('disconnected: ' + nodeId)
|
|
193
193
|
this.connections.delete(nodeId)
|
|
194
194
|
if (this.config.isLayer0) {
|
|
@@ -202,11 +202,7 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
|
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
-
|
|
206
|
-
this.removeContact(nodeId)
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
private removeContact(nodeId: DhtAddress): void {
|
|
205
|
+
removeContact(nodeId: DhtAddress): void {
|
|
210
206
|
if (this.stopped) {
|
|
211
207
|
return
|
|
212
208
|
}
|
|
@@ -274,16 +270,11 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
|
|
|
274
270
|
return this.bucket.toArray().map((rpcRemote: DhtNodeRpcRemote) => rpcRemote.getPeerDescriptor())
|
|
275
271
|
}
|
|
276
272
|
|
|
277
|
-
|
|
273
|
+
setContactActive(nodeId: DhtAddress): void {
|
|
278
274
|
this.contacts.setActive(nodeId)
|
|
279
275
|
}
|
|
280
276
|
|
|
281
|
-
|
|
282
|
-
this.bucket.remove(getRawFromDhtAddress(nodeId))
|
|
283
|
-
this.contacts.removeContact(nodeId)
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
handleNewPeers(peerDescriptors: PeerDescriptor[], setActive?: boolean): void {
|
|
277
|
+
addContact(peerDescriptors: PeerDescriptor[], setActive?: boolean): void {
|
|
287
278
|
if (this.stopped) {
|
|
288
279
|
return
|
|
289
280
|
}
|
|
@@ -13,7 +13,7 @@ export class ContactState<C> {
|
|
|
13
13
|
|
|
14
14
|
export interface Events<C> {
|
|
15
15
|
contactRemoved: (removedContact: C, closestContacts: C[]) => void
|
|
16
|
-
|
|
16
|
+
contactAdded: (contactAdded: C, closestContacts: C[]) => void
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export class ContactList<C extends { getNodeId: () => DhtAddress }> extends EventEmitter<Events<C>> {
|
|
@@ -29,7 +29,7 @@ export class RandomContactList<C extends { getNodeId: () => DhtAddress }> extend
|
|
|
29
29
|
this.contactIds.push(contact.getNodeId())
|
|
30
30
|
this.contactsById.set(contact.getNodeId(), new ContactState(contact))
|
|
31
31
|
this.emit(
|
|
32
|
-
'
|
|
32
|
+
'contactAdded',
|
|
33
33
|
contact,
|
|
34
34
|
this.getContacts()
|
|
35
35
|
)
|
|
@@ -40,45 +40,47 @@ export class SortedContactList<C extends { getNodeId: () => DhtAddress }> extend
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
public addContact(contact: C): void {
|
|
43
|
-
|
|
43
|
+
const contactId = contact.getNodeId()
|
|
44
|
+
if (this.config.excludedNodeIds !== undefined && this.config.excludedNodeIds.has(contactId)) {
|
|
44
45
|
return
|
|
45
46
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
(this.config.nodeIdDistanceLimit !== undefined && this.compareIds(this.config.nodeIdDistanceLimit, contact.getNodeId()) < 0)) {
|
|
47
|
+
if ((!this.config.allowToContainReferenceId && (this.config.referenceId === contactId)) ||
|
|
48
|
+
(this.config.nodeIdDistanceLimit !== undefined && this.compareIds(this.config.nodeIdDistanceLimit, contactId) < 0)) {
|
|
49
49
|
return
|
|
50
50
|
}
|
|
51
|
-
if (!this.contactsById.has(
|
|
51
|
+
if (!this.contactsById.has(contactId)) {
|
|
52
52
|
if ((this.config.maxSize === undefined) || (this.contactIds.length < this.config.maxSize)) {
|
|
53
|
-
this.contactsById.set(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
this.contactsById.set(contactId, new ContactState(contact))
|
|
54
|
+
const index = sortedIndexBy(this.contactIds, contactId, (id: DhtAddress) => { return this.distanceToReferenceId(id) })
|
|
55
|
+
this.contactIds.splice(index, 0, contactId)
|
|
56
|
+
if (this.config.emitEvents) {
|
|
57
|
+
this.emit(
|
|
58
|
+
'contactAdded',
|
|
59
|
+
contact,
|
|
60
|
+
this.getClosestContacts()
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
} else if (this.compareIds(this.contactIds[this.config.maxSize - 1], contactId) > 0) {
|
|
59
64
|
const removedId = this.contactIds.pop()
|
|
60
65
|
const removedContact = this.contactsById.get(removedId!)!.contact
|
|
61
66
|
this.contactsById.delete(removedId!)
|
|
62
|
-
this.contactsById.set(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const index = sortedIndexBy(this.contactIds, contact.getNodeId(), (id: DhtAddress) => { return this.distanceToReferenceId(id) })
|
|
66
|
-
this.contactIds.splice(index, 0, contact.getNodeId())
|
|
67
|
+
this.contactsById.set(contactId, new ContactState(contact))
|
|
68
|
+
const index = sortedIndexBy(this.contactIds, contactId, (id: DhtAddress) => { return this.distanceToReferenceId(id) })
|
|
69
|
+
this.contactIds.splice(index, 0, contactId)
|
|
67
70
|
if (this.config.emitEvents) {
|
|
71
|
+
const closestContacts = this.getClosestContacts()
|
|
68
72
|
this.emit(
|
|
69
73
|
'contactRemoved',
|
|
70
74
|
removedContact,
|
|
71
|
-
|
|
75
|
+
closestContacts
|
|
76
|
+
)
|
|
77
|
+
this.emit(
|
|
78
|
+
'contactAdded',
|
|
79
|
+
contact,
|
|
80
|
+
closestContacts
|
|
72
81
|
)
|
|
73
82
|
}
|
|
74
83
|
}
|
|
75
|
-
if (this.config.emitEvents) {
|
|
76
|
-
this.emit(
|
|
77
|
-
'newContact',
|
|
78
|
-
contact,
|
|
79
|
-
this.getClosestContacts()
|
|
80
|
-
)
|
|
81
|
-
}
|
|
82
84
|
}
|
|
83
85
|
}
|
|
84
86
|
|
|
@@ -34,11 +34,11 @@ export class DiscoverySession {
|
|
|
34
34
|
this.config = config
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
private
|
|
37
|
+
private addContacts(contacts: PeerDescriptor[]): void {
|
|
38
38
|
if (this.stopped) {
|
|
39
39
|
return
|
|
40
40
|
}
|
|
41
|
-
this.config.peerManager.
|
|
41
|
+
this.config.peerManager.addContact(contacts)
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
private async getClosestPeersFromContact(contact: DhtNodeRpcRemote): Promise<PeerDescriptor[]> {
|
|
@@ -48,7 +48,7 @@ export class DiscoverySession {
|
|
|
48
48
|
logger.trace(`Getting closest peers from contact: ${getNodeIdFromPeerDescriptor(contact.getPeerDescriptor())}`)
|
|
49
49
|
this.config.contactedPeers.add(contact.getNodeId())
|
|
50
50
|
const returnedContacts = await contact.getClosestPeers(this.config.targetId)
|
|
51
|
-
this.config.peerManager.
|
|
51
|
+
this.config.peerManager.setContactActive(contact.getNodeId())
|
|
52
52
|
return returnedContacts
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -60,7 +60,7 @@ export class DiscoverySession {
|
|
|
60
60
|
const targetId = getRawFromDhtAddress(this.config.targetId)
|
|
61
61
|
const oldClosestNeighbor = this.config.peerManager.getClosestNeighborsTo(this.config.targetId, 1)[0]
|
|
62
62
|
const oldClosestDistance = getDistance(targetId, getRawFromDhtAddress(oldClosestNeighbor.getNodeId()))
|
|
63
|
-
this.
|
|
63
|
+
this.addContacts(contacts)
|
|
64
64
|
const newClosestNeighbor = this.config.peerManager.getClosestNeighborsTo(this.config.targetId, 1)[0]
|
|
65
65
|
const newClosestDistance = getDistance(targetId, getRawFromDhtAddress(newClosestNeighbor.getNodeId()))
|
|
66
66
|
if (newClosestDistance >= oldClosestDistance) {
|
|
@@ -73,7 +73,7 @@ export class DiscoverySession {
|
|
|
73
73
|
return
|
|
74
74
|
}
|
|
75
75
|
this.ongoingClosestPeersRequests.delete(peer.getNodeId())
|
|
76
|
-
this.config.peerManager.
|
|
76
|
+
this.config.peerManager.removeContact(peer.getNodeId())
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
private findMoreContacts(): void {
|
|
@@ -75,7 +75,7 @@ export class PeerDiscovery {
|
|
|
75
75
|
return
|
|
76
76
|
}
|
|
77
77
|
this.config.connectionManager?.lockConnection(entryPointDescriptor, `${this.config.serviceId}::joinDht`)
|
|
78
|
-
this.config.peerManager.
|
|
78
|
+
this.config.peerManager.addContact([entryPointDescriptor])
|
|
79
79
|
const targetId = getNodeIdFromPeerDescriptor(this.config.localPeerDescriptor)
|
|
80
80
|
const sessions = [this.createSession(targetId, contactedPeers)]
|
|
81
81
|
if (additionalDistantJoin.enabled) {
|
|
@@ -162,7 +162,7 @@ export class PeerDiscovery {
|
|
|
162
162
|
await Promise.allSettled(
|
|
163
163
|
nodes.map(async (peer: DhtNodeRpcRemote) => {
|
|
164
164
|
const contacts = await peer.getClosestPeers(localNodeId)
|
|
165
|
-
this.config.peerManager.
|
|
165
|
+
this.config.peerManager.addContact(contacts)
|
|
166
166
|
})
|
|
167
167
|
)
|
|
168
168
|
}
|
|
@@ -28,7 +28,6 @@ export class RecursiveOperationRpcLocal implements IRecursiveOperationRpc {
|
|
|
28
28
|
}
|
|
29
29
|
const senderId = getNodeIdFromPeerDescriptor(getPreviousPeer(routedMessage) ?? routedMessage.sourcePeer!)
|
|
30
30
|
logger.trace(`Received routeRequest call from ${senderId}`)
|
|
31
|
-
this.config.addContact(routedMessage.sourcePeer!, true)
|
|
32
31
|
this.config.addToDuplicateDetector(routedMessage.requestId)
|
|
33
32
|
return this.config.doRouteRequest(routedMessage)
|
|
34
33
|
}
|
|
@@ -5,6 +5,7 @@ import { IRouterRpc } from '../../proto/packages/dht/protos/DhtRpc.server'
|
|
|
5
5
|
import { DuplicateDetector } from './DuplicateDetector'
|
|
6
6
|
import { RoutingMode } from './RoutingSession'
|
|
7
7
|
import { areEqualPeerDescriptors, getDhtAddressFromRaw, getNodeIdFromPeerDescriptor } from '../../identifiers'
|
|
8
|
+
import { v4 } from 'uuid'
|
|
8
9
|
|
|
9
10
|
interface RouterRpcLocalConfig {
|
|
10
11
|
doRouteMessage: (routedMessage: RouteMessageWrapper, mode?: RoutingMode) => RouteMessageAck
|
|
@@ -40,7 +41,6 @@ export class RouterRpcLocal implements IRouterRpc {
|
|
|
40
41
|
return createRouteMessageAck(routedMessage, RouteMessageError.DUPLICATE)
|
|
41
42
|
}
|
|
42
43
|
logger.trace(`Processing received routeMessage ${routedMessage.requestId}`)
|
|
43
|
-
this.config.addContact(routedMessage.sourcePeer!, true)
|
|
44
44
|
this.config.duplicateRequestDetector.add(routedMessage.requestId)
|
|
45
45
|
if (areEqualBinaries(this.config.localPeerDescriptor.nodeId, routedMessage.target)) {
|
|
46
46
|
logger.trace(`routing message targeted to self ${routedMessage.requestId}`)
|
|
@@ -59,7 +59,6 @@ export class RouterRpcLocal implements IRouterRpc {
|
|
|
59
59
|
return createRouteMessageAck(forwardMessage, RouteMessageError.DUPLICATE)
|
|
60
60
|
}
|
|
61
61
|
logger.trace(`Processing received forward routeMessage ${forwardMessage.requestId}`)
|
|
62
|
-
this.config.addContact(forwardMessage.sourcePeer!, true)
|
|
63
62
|
this.config.duplicateRequestDetector.add(forwardMessage.requestId)
|
|
64
63
|
if (areEqualBinaries(this.config.localPeerDescriptor.nodeId, forwardMessage.target)) {
|
|
65
64
|
return this.forwardToDestination(forwardMessage)
|
|
@@ -75,7 +74,7 @@ export class RouterRpcLocal implements IRouterRpc {
|
|
|
75
74
|
this.config.connectionManager?.handleMessage(forwardedMessage)
|
|
76
75
|
return createRouteMessageAck(routedMessage)
|
|
77
76
|
}
|
|
78
|
-
return this.config.doRouteMessage({ ...routedMessage, target: forwardedMessage.targetDescriptor!.nodeId })
|
|
77
|
+
return this.config.doRouteMessage({ ...routedMessage, requestId: v4(), target: forwardedMessage.targetDescriptor!.nodeId })
|
|
79
78
|
}
|
|
80
79
|
|
|
81
80
|
}
|
|
@@ -17,6 +17,7 @@ import { pull } from 'lodash'
|
|
|
17
17
|
const logger = new Logger(module)
|
|
18
18
|
|
|
19
19
|
const MAX_FAILED_HOPS = 2
|
|
20
|
+
const CONTACT_LIST_MAX_SIZE = 10
|
|
20
21
|
|
|
21
22
|
class RemoteContact extends Contact {
|
|
22
23
|
|
|
@@ -90,7 +91,7 @@ export class RoutingSession extends EventEmitter<RoutingSessionEvents> {
|
|
|
90
91
|
const previousId = previousPeer ? getNodeIdFromPeerDescriptor(previousPeer) : undefined
|
|
91
92
|
this.contactList = new SortedContactList({
|
|
92
93
|
referenceId: getDhtAddressFromRaw(config.routedMessage.target),
|
|
93
|
-
maxSize:
|
|
94
|
+
maxSize: CONTACT_LIST_MAX_SIZE,
|
|
94
95
|
allowToContainReferenceId: true,
|
|
95
96
|
nodeIdDistanceLimit: previousId,
|
|
96
97
|
excludedNodeIds: config.excludedNodeIds,
|
|
@@ -57,11 +57,8 @@ export class LocalDataStore {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
public
|
|
61
|
-
|
|
62
|
-
if (storedEntry) {
|
|
63
|
-
storedEntry.stale = stale
|
|
64
|
-
}
|
|
60
|
+
public keys(): IterableIterator<DhtAddress> {
|
|
61
|
+
return this.store.keys()
|
|
65
62
|
}
|
|
66
63
|
|
|
67
64
|
public setAllEntriesAsStale(key: DhtAddress): void {
|
|
@@ -13,7 +13,6 @@ import { Timestamp } from '../../proto/google/protobuf/timestamp'
|
|
|
13
13
|
import { SortedContactList } from '../contact/SortedContactList'
|
|
14
14
|
import { Contact } from '../contact/Contact'
|
|
15
15
|
import { ServiceID } from '../../types/ServiceID'
|
|
16
|
-
import { findIndex } from 'lodash'
|
|
17
16
|
import { DhtAddress, areEqualPeerDescriptors, getDhtAddressFromRaw, getNodeIdFromPeerDescriptor, getRawFromDhtAddress } from '../../identifiers'
|
|
18
17
|
import { StoreRpcLocal } from './StoreRpcLocal'
|
|
19
18
|
import { getDistance } from '../PeerManager'
|
|
@@ -53,20 +52,18 @@ export class StoreManager {
|
|
|
53
52
|
(request: ReplicateDataRequest, context: ServerCallContext) => rpcLocal.replicateData(request, context))
|
|
54
53
|
}
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
for (const
|
|
58
|
-
this.replicateAndUpdateStaleState(
|
|
55
|
+
onContactAdded(peerDescriptor: PeerDescriptor): void {
|
|
56
|
+
for (const key of this.config.localDataStore.keys()) {
|
|
57
|
+
this.replicateAndUpdateStaleState(key, peerDescriptor)
|
|
59
58
|
}
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
private replicateAndUpdateStaleState(
|
|
61
|
+
private replicateAndUpdateStaleState(key: DhtAddress, newNode: PeerDescriptor): void {
|
|
63
62
|
const newNodeId = getNodeIdFromPeerDescriptor(newNode)
|
|
64
|
-
const
|
|
65
|
-
// TODO use config option or named constant?
|
|
66
|
-
const closestToData = this.config.getClosestNeighborsTo(key, 10)
|
|
63
|
+
const closestToData = this.config.getClosestNeighborsTo(key, this.config.redundancyFactor)
|
|
67
64
|
const sortedList = new SortedContactList<Contact>({
|
|
68
65
|
referenceId: key,
|
|
69
|
-
maxSize:
|
|
66
|
+
maxSize: this.config.redundancyFactor,
|
|
70
67
|
allowToContainReferenceId: true,
|
|
71
68
|
emitEvents: false
|
|
72
69
|
})
|
|
@@ -79,18 +76,14 @@ export class StoreManager {
|
|
|
79
76
|
const selfIsPrimaryStorer = (sortedList.getClosestContactId() === getNodeIdFromPeerDescriptor(this.config.localPeerDescriptor))
|
|
80
77
|
if (selfIsPrimaryStorer) {
|
|
81
78
|
sortedList.addContact(new Contact(newNode))
|
|
82
|
-
|
|
83
|
-
// findIndex should never return -1 here because we just added the new node to the list
|
|
84
|
-
const index = findIndex(sorted, (nodeId) => (nodeId === newNodeId))
|
|
85
|
-
// if new node is within the storageRedundancyFactor closest nodes to the data
|
|
86
|
-
// do replicate data to it
|
|
87
|
-
if (index < this.config.redundancyFactor) {
|
|
79
|
+
if (sortedList.getContact(newNodeId) !== undefined) {
|
|
88
80
|
setImmediate(async () => {
|
|
89
|
-
|
|
81
|
+
const dataEntries = Array.from(this.config.localDataStore.values(key))
|
|
82
|
+
await Promise.all(dataEntries.map(async (dataEntry) => this.replicateDataToContact(dataEntry, newNode)))
|
|
90
83
|
})
|
|
91
84
|
}
|
|
92
85
|
} else if (!this.selfIsWithinRedundancyFactor(key)) {
|
|
93
|
-
this.config.localDataStore.
|
|
86
|
+
this.config.localDataStore.setAllEntriesAsStale(key)
|
|
94
87
|
}
|
|
95
88
|
}
|
|
96
89
|
|
package/src/identifiers.ts
CHANGED
|
@@ -3,7 +3,7 @@ import crypto from 'crypto'
|
|
|
3
3
|
import { PeerDescriptor } from './proto/packages/dht/protos/DhtRpc'
|
|
4
4
|
|
|
5
5
|
// https://www.scs.stanford.edu/~dm/home/papers/kpos.pdf
|
|
6
|
-
const KADEMLIA_ID_LENGTH_IN_BYTES = 20
|
|
6
|
+
export const KADEMLIA_ID_LENGTH_IN_BYTES = 20
|
|
7
7
|
|
|
8
8
|
export type DhtAddress = BrandedString<'DhtAddress'>
|
|
9
9
|
export type DhtAddressRaw = Uint8Array
|