@streamr/dht 100.2.1 → 100.2.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 +6 -6
- package/dist/src/connection/ConnectionLockStates.js +4 -6
- package/dist/src/connection/ConnectionLockStates.js.map +1 -1
- package/dist/src/connection/ConnectionManager.d.ts +1 -0
- package/dist/src/connection/ConnectionManager.js +8 -2
- package/dist/src/connection/ConnectionManager.js.map +1 -1
- package/dist/src/connection/ManagedConnection.js +7 -9
- package/dist/src/connection/ManagedConnection.js.map +1 -1
- package/dist/src/connection/connectivityChecker.js +0 -3
- package/dist/src/connection/connectivityChecker.js.map +1 -1
- package/dist/src/connection/websocket/WebsocketConnector.js +1 -1
- package/dist/src/connection/websocket/WebsocketConnector.js.map +1 -1
- package/dist/src/dht/DhtNode.d.ts +10 -7
- package/dist/src/dht/DhtNode.js +51 -32
- package/dist/src/dht/DhtNode.js.map +1 -1
- package/dist/src/dht/DhtNodeRpcLocal.d.ts +2 -2
- package/dist/src/dht/DhtNodeRpcLocal.js +5 -4
- package/dist/src/dht/DhtNodeRpcLocal.js.map +1 -1
- package/dist/src/dht/DhtNodeRpcRemote.js +1 -0
- package/dist/src/dht/DhtNodeRpcRemote.js.map +1 -1
- package/dist/src/dht/PeerManager.d.ts +14 -14
- package/dist/src/dht/PeerManager.js +23 -59
- package/dist/src/dht/PeerManager.js.map +1 -1
- package/dist/src/dht/contact/ContactList.d.ts +2 -2
- package/dist/src/dht/contact/RandomContactList.js +2 -2
- package/dist/src/dht/contact/RandomContactList.js.map +1 -1
- package/dist/src/dht/contact/RingContactList.d.ts +2 -5
- package/dist/src/dht/contact/RingContactList.js +3 -11
- package/dist/src/dht/contact/RingContactList.js.map +1 -1
- package/dist/src/dht/contact/SortedContactList.d.ts +4 -1
- package/dist/src/dht/contact/SortedContactList.js +4 -5
- package/dist/src/dht/contact/SortedContactList.js.map +1 -1
- package/dist/src/dht/contact/getClosestContacts.d.ts +7 -0
- package/dist/src/dht/contact/getClosestContacts.js +18 -0
- package/dist/src/dht/contact/getClosestContacts.js.map +1 -0
- package/dist/src/dht/discovery/DiscoverySession.d.ts +8 -8
- package/dist/src/dht/discovery/DiscoverySession.js +30 -35
- package/dist/src/dht/discovery/DiscoverySession.js.map +1 -1
- package/dist/src/dht/discovery/PeerDiscovery.d.ts +1 -2
- package/dist/src/dht/discovery/PeerDiscovery.js +6 -8
- package/dist/src/dht/discovery/PeerDiscovery.js.map +1 -1
- package/dist/src/dht/discovery/RingDiscoverySession.d.ts +4 -4
- package/dist/src/dht/discovery/RingDiscoverySession.js +13 -13
- package/dist/src/dht/discovery/RingDiscoverySession.js.map +1 -1
- package/dist/src/dht/recursive-operation/RecursiveOperationManager.d.ts +2 -2
- package/dist/src/dht/recursive-operation/RecursiveOperationManager.js +15 -15
- package/dist/src/dht/recursive-operation/RecursiveOperationManager.js.map +1 -1
- package/dist/src/dht/recursive-operation/RecursiveOperationSession.d.ts +1 -1
- package/dist/src/dht/recursive-operation/RecursiveOperationSession.js +11 -13
- package/dist/src/dht/recursive-operation/RecursiveOperationSession.js.map +1 -1
- package/dist/src/dht/recursive-operation/RecursiveOperationSessionRpcLocal.js +1 -1
- package/dist/src/dht/recursive-operation/RecursiveOperationSessionRpcRemote.d.ts +1 -1
- package/dist/src/dht/recursive-operation/RecursiveOperationSessionRpcRemote.js +2 -2
- package/dist/src/dht/recursive-operation/RecursiveOperationSessionRpcRemote.js.map +1 -1
- package/dist/src/dht/routing/Router.d.ts +1 -2
- package/dist/src/dht/routing/Router.js +2 -3
- package/dist/src/dht/routing/Router.js.map +1 -1
- package/dist/src/dht/routing/RoutingSession.d.ts +1 -2
- package/dist/src/dht/routing/RoutingSession.js +2 -2
- package/dist/src/dht/routing/RoutingSession.js.map +1 -1
- package/dist/src/dht/store/StoreRpcLocal.js +3 -3
- package/dist/src/dht/store/StoreRpcLocal.js.map +1 -1
- package/dist/src/proto/packages/dht/protos/DhtRpc.client.d.ts +8 -0
- package/dist/src/proto/packages/dht/protos/DhtRpc.client.js +4 -0
- package/dist/src/proto/packages/dht/protos/DhtRpc.client.js.map +1 -1
- package/dist/src/proto/packages/dht/protos/DhtRpc.d.ts +10 -2
- package/dist/src/proto/packages/dht/protos/DhtRpc.js +1 -1
- package/dist/src/proto/packages/dht/protos/DhtRpc.js.map +1 -1
- package/dist/src/proto/packages/dht/protos/DhtRpc.server.d.ts +4 -0
- package/dist/src/transport/ITransport.d.ts +3 -0
- package/dist/src/transport/ITransport.js.map +1 -1
- package/package.json +6 -6
- package/protos/DhtRpc.proto +7 -1
- package/src/connection/ConnectionLockStates.ts +3 -5
- package/src/connection/ConnectionManager.ts +9 -2
- package/src/connection/ManagedConnection.ts +11 -14
- package/src/connection/connectivityChecker.ts +0 -2
- package/src/connection/webrtc/BrowserWebrtcConnection.ts +0 -1
- package/src/connection/websocket/WebsocketConnector.ts +1 -1
- package/src/dht/DhtNode.ts +63 -44
- package/src/dht/DhtNodeRpcLocal.ts +7 -6
- package/src/dht/DhtNodeRpcRemote.ts +1 -0
- package/src/dht/PeerManager.ts +38 -73
- package/src/dht/contact/ContactList.ts +2 -2
- package/src/dht/contact/RandomContactList.ts +2 -3
- package/src/dht/contact/RingContactList.ts +6 -16
- package/src/dht/contact/SortedContactList.ts +9 -10
- package/src/dht/contact/getClosestContacts.ts +22 -0
- package/src/dht/discovery/DiscoverySession.ts +35 -45
- package/src/dht/discovery/PeerDiscovery.ts +6 -9
- package/src/dht/discovery/RingDiscoverySession.ts +13 -13
- package/src/dht/recursive-operation/RecursiveOperationManager.ts +16 -16
- package/src/dht/recursive-operation/RecursiveOperationSession.ts +11 -13
- package/src/dht/recursive-operation/RecursiveOperationSessionRpcLocal.ts +1 -1
- package/src/dht/recursive-operation/RecursiveOperationSessionRpcRemote.ts +2 -2
- package/src/dht/routing/Router.ts +3 -5
- package/src/dht/routing/RoutingSession.ts +3 -4
- package/src/dht/store/StoreRpcLocal.ts +3 -3
- package/src/proto/packages/dht/protos/DhtRpc.client.ts +8 -0
- package/src/proto/packages/dht/protos/DhtRpc.server.ts +4 -0
- package/src/proto/packages/dht/protos/DhtRpc.ts +11 -3
- package/src/transport/ITransport.ts +3 -0
- package/test/end-to-end/Layer0MixedConnectionTypes.test.ts +3 -5
- package/test/integration/DhtNode.test.ts +81 -0
- package/test/integration/Layer1-scale.test.ts +1 -1
- package/test/integration/ScaleDownDht.test.ts +7 -4
- package/test/unit/DiscoverySession.test.ts +85 -0
- package/test/unit/PeerManager.test.ts +3 -17
- package/test/unit/RecursiveOperationManager.test.ts +5 -3
- package/test/unit/Router.test.ts +2 -2
- package/test/unit/RoutingSession.test.ts +2 -2
- package/test/unit/SortedContactList.test.ts +4 -4
- package/test/unit/getClosestContacts.test.ts +28 -0
- package/test/utils/FakeTransport.ts +34 -7
- package/test/utils/mock/Router.ts +0 -3
- package/test/utils/mock/Transport.ts +10 -0
- package/test/utils/topology.ts +80 -0
- package/test/RandomGraphSimulation.ts +0 -53
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { EventEmitter } from 'eventemitter3'
|
|
2
|
-
import {
|
|
2
|
+
import { DhtAddress, getDhtAddressFromRaw, getNodeIdFromPeerDescriptor } from '../../src/identifiers'
|
|
3
3
|
import { Message, PeerDescriptor } from '../../src/proto/packages/dht/protos/DhtRpc'
|
|
4
|
+
import { DEFAULT_SEND_OPTIONS, ITransport, SendOptions, TransportEvents } from '../../src/transport/ITransport'
|
|
4
5
|
|
|
5
6
|
class FakeTransport extends EventEmitter<TransportEvents> implements ITransport {
|
|
6
7
|
|
|
7
8
|
private onSend: (msg: Message) => void
|
|
8
9
|
private readonly localPeerDescriptor: PeerDescriptor
|
|
10
|
+
// currently adds a peerDescription to the connections array when a "connect" option is seen in
|
|
11
|
+
// in send() call and never disconnects (TODO could add some disconnection logic? and maybe
|
|
12
|
+
// the connection should be seen by another FakeTransport instance, too?)
|
|
13
|
+
private connections: PeerDescriptor[] = []
|
|
9
14
|
|
|
10
15
|
constructor(peerDescriptor: PeerDescriptor, onSend: (msg: Message) => void) {
|
|
11
16
|
super()
|
|
@@ -13,19 +18,37 @@ class FakeTransport extends EventEmitter<TransportEvents> implements ITransport
|
|
|
13
18
|
this.localPeerDescriptor = peerDescriptor
|
|
14
19
|
}
|
|
15
20
|
|
|
16
|
-
async send(msg: Message): Promise<void> {
|
|
21
|
+
async send(msg: Message, opts?: SendOptions): Promise<void> {
|
|
22
|
+
const connect = opts?.connect ?? DEFAULT_SEND_OPTIONS.connect
|
|
23
|
+
const targetNodeId = getNodeIdFromPeerDescriptor(msg.targetDescriptor!)
|
|
24
|
+
if (connect && !this.connections.some((c) => getNodeIdFromPeerDescriptor(c) === targetNodeId)) {
|
|
25
|
+
this.connect(msg.targetDescriptor!)
|
|
26
|
+
}
|
|
17
27
|
msg.sourceDescriptor = this.localPeerDescriptor
|
|
18
28
|
this.onSend(msg)
|
|
19
29
|
}
|
|
20
30
|
|
|
21
|
-
// eslint-disable-next-line class-methods-use-this
|
|
22
31
|
getLocalPeerDescriptor(): PeerDescriptor {
|
|
23
|
-
|
|
32
|
+
return this.localPeerDescriptor
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private connect(peerDescriptor: PeerDescriptor) {
|
|
36
|
+
this.connections.push(peerDescriptor)
|
|
37
|
+
this.emit('connected', peerDescriptor)
|
|
24
38
|
}
|
|
25
39
|
|
|
26
|
-
// eslint-disable-next-line class-methods-use-this
|
|
27
40
|
getConnections(): PeerDescriptor[] {
|
|
28
|
-
|
|
41
|
+
return this.connections
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// eslint-disable-next-line class-methods-use-this
|
|
45
|
+
getConnectionCount(): number {
|
|
46
|
+
return this.connections.length
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// eslint-disable-next-line class-methods-use-this
|
|
50
|
+
hasConnection(nodeId: DhtAddress): boolean {
|
|
51
|
+
return this.connections.some((c) => getNodeIdFromPeerDescriptor(c) === nodeId)
|
|
29
52
|
}
|
|
30
53
|
|
|
31
54
|
// eslint-disable-next-line class-methods-use-this
|
|
@@ -39,7 +62,11 @@ export class FakeEnvironment {
|
|
|
39
62
|
|
|
40
63
|
createTransport(peerDescriptor: PeerDescriptor): ITransport {
|
|
41
64
|
const transport = new FakeTransport(peerDescriptor, (msg) => {
|
|
42
|
-
|
|
65
|
+
const targetNode = getDhtAddressFromRaw(msg.targetDescriptor!.nodeId)
|
|
66
|
+
const targetTransport = this.transports.find((t) => getNodeIdFromPeerDescriptor(t.getLocalPeerDescriptor()) === targetNode)
|
|
67
|
+
if (targetTransport !== undefined) {
|
|
68
|
+
targetTransport.emit('message', msg)
|
|
69
|
+
}
|
|
43
70
|
})
|
|
44
71
|
this.transports.push(transport)
|
|
45
72
|
return transport
|
|
@@ -6,17 +6,14 @@ export class MockRouter implements Methods<Router> {
|
|
|
6
6
|
|
|
7
7
|
// eslint-disable-next-line class-methods-use-this
|
|
8
8
|
addRoutingSession(): void {
|
|
9
|
-
return
|
|
10
9
|
}
|
|
11
10
|
|
|
12
11
|
// eslint-disable-next-line class-methods-use-this
|
|
13
12
|
removeRoutingSession(): void {
|
|
14
|
-
return
|
|
15
13
|
}
|
|
16
14
|
|
|
17
15
|
// eslint-disable-next-line class-methods-use-this
|
|
18
16
|
addToDuplicateDetector(): void {
|
|
19
|
-
return
|
|
20
17
|
}
|
|
21
18
|
|
|
22
19
|
// eslint-disable-next-line class-methods-use-this
|
|
@@ -19,6 +19,16 @@ export class MockTransport extends EventEmitter<TransportEvents> implements ITra
|
|
|
19
19
|
return []
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
// eslint-disable-next-line class-methods-use-this
|
|
23
|
+
getConnectionCount(): number {
|
|
24
|
+
return 0
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// eslint-disable-next-line class-methods-use-this
|
|
28
|
+
hasConnection(): boolean {
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
31
|
+
|
|
22
32
|
// eslint-disable-next-line class-methods-use-this
|
|
23
33
|
stop(): void {
|
|
24
34
|
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Multimap } from '@streamr/utils'
|
|
2
|
+
import { DhtAddress, createRandomDhtAddress, getRawFromDhtAddress } from '../../src/identifiers'
|
|
3
|
+
import { minBy, range, without } from 'lodash'
|
|
4
|
+
import { SortedContactList } from '../../src/dht/contact/SortedContactList'
|
|
5
|
+
import { getDistance } from '../../src/dht/PeerManager'
|
|
6
|
+
|
|
7
|
+
export const getTopologyPartitions = (topology: Multimap<DhtAddress, DhtAddress>): Set<DhtAddress>[] => {
|
|
8
|
+
let partitions: Set<DhtAddress>[] = []
|
|
9
|
+
for (const nodeId of topology.keys()) {
|
|
10
|
+
const neighbors = topology.get(nodeId)
|
|
11
|
+
const existingPartition = partitions.find((partition) => partition.has(nodeId))
|
|
12
|
+
if (existingPartition !== undefined) {
|
|
13
|
+
for (const neighbor of neighbors) {
|
|
14
|
+
if (!existingPartition.has(neighbor)) {
|
|
15
|
+
const otherPartition = partitions.find((partition) => partition.has(neighbor))
|
|
16
|
+
if (otherPartition !== undefined) {
|
|
17
|
+
for (const otherNode of otherPartition) {
|
|
18
|
+
existingPartition.add(otherNode)
|
|
19
|
+
}
|
|
20
|
+
partitions = without(partitions, otherPartition)
|
|
21
|
+
} else {
|
|
22
|
+
existingPartition.add(neighbor)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
const partition = new Set([nodeId, ...neighbors])
|
|
28
|
+
partitions.push(partition)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return partitions
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const getClosestNodes = (referenceId: DhtAddress, nodeIds: DhtAddress[], count: number, allowToContainReferenceId: boolean): DhtAddress[] => {
|
|
35
|
+
const list = new SortedContactList({
|
|
36
|
+
referenceId,
|
|
37
|
+
allowToContainReferenceId,
|
|
38
|
+
maxSize: count
|
|
39
|
+
})
|
|
40
|
+
list.addContacts(nodeIds.map((n) => ({ getNodeId: () => n })))
|
|
41
|
+
return list.getClosestContacts().map((c) => c.getNodeId())
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/*
|
|
45
|
+
* There are no network splits, and each node has only neighbors which are globally closest
|
|
46
|
+
* to the node's ID.
|
|
47
|
+
*/
|
|
48
|
+
export const createTestTopology = (nodeCount: number, minNeighorCount: number): Multimap<DhtAddress, DhtAddress> => {
|
|
49
|
+
const topology: Multimap<DhtAddress, DhtAddress> = new Multimap()
|
|
50
|
+
const nodeIds = range(nodeCount).map(() => createRandomDhtAddress())
|
|
51
|
+
for (const nodeId of nodeIds) {
|
|
52
|
+
const closestNodes = getClosestNodes(nodeId, nodeIds, minNeighorCount, false)
|
|
53
|
+
for (const closestNode of closestNodes) {
|
|
54
|
+
if (!topology.has(nodeId, closestNode)) {
|
|
55
|
+
topology.add(nodeId, closestNode)
|
|
56
|
+
}
|
|
57
|
+
if (!topology.has(closestNode, nodeId)) {
|
|
58
|
+
topology.add(closestNode, nodeId)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// eslint-disable-next-line no-constant-condition
|
|
63
|
+
while (true) {
|
|
64
|
+
const partitions = getTopologyPartitions(topology)
|
|
65
|
+
if (partitions.length === 1) {
|
|
66
|
+
break
|
|
67
|
+
} else {
|
|
68
|
+
const closestPairs = nodeIds.map((nodeId: DhtAddress) => {
|
|
69
|
+
const ownPartition = partitions.find((partition) => partition.has(nodeId))!
|
|
70
|
+
const otherNodes = without(nodeIds, ...[...ownPartition])
|
|
71
|
+
const closestNodedId = getClosestNodes(nodeId, otherNodes, 1, false)[0]
|
|
72
|
+
return [nodeId, closestNodedId]
|
|
73
|
+
})
|
|
74
|
+
const mergePair = minBy(closestPairs, (pair) => getDistance(getRawFromDhtAddress(pair[0]), getRawFromDhtAddress(pair[1])))!
|
|
75
|
+
topology.add(mergePair[0], mergePair[1])
|
|
76
|
+
topology.add(mergePair[1], mergePair[0])
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return topology
|
|
80
|
+
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
|
|
3
|
-
import KBucket from 'k-bucket'
|
|
4
|
-
import { range } from 'lodash'
|
|
5
|
-
import { DhtAddressRaw } from '../src/identifiers'
|
|
6
|
-
|
|
7
|
-
const compareIds = (id1: DhtAddressRaw, id2: DhtAddressRaw) => {
|
|
8
|
-
const sorted = [id1, id2].sort()
|
|
9
|
-
return KBucket.distance(sorted[0], sorted[1])
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const generateIDs = (count: number): DhtAddressRaw[] => {
|
|
13
|
-
return range(count).map((i) => new Uint8Array([i, 1, 1]))
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const count = 9
|
|
17
|
-
const ids = generateIDs(count)
|
|
18
|
-
|
|
19
|
-
const distances = ids.map((id1) => ids.map((id2) => {
|
|
20
|
-
return {
|
|
21
|
-
distance: compareIds(id1, id2),
|
|
22
|
-
id: id2.toString()
|
|
23
|
-
}
|
|
24
|
-
}))
|
|
25
|
-
|
|
26
|
-
const graph: any[] = []
|
|
27
|
-
for (let i = 0; i < count; i++) {
|
|
28
|
-
const sorted = distances[i].sort((a, b) => a.distance - b.distance)
|
|
29
|
-
graph.push([sorted[1], sorted[2], sorted[3], sorted[4]])
|
|
30
|
-
}
|
|
31
|
-
const result = new Map<string, number>()
|
|
32
|
-
ids.forEach((key) => {
|
|
33
|
-
result.set(key.toString(), 0)
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
for (let i = 0; i < count; i++) {
|
|
37
|
-
graph[i].forEach((val: any) => {
|
|
38
|
-
result.set(val.id, result.get(val.id)! + 1)
|
|
39
|
-
})
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const bidirectional = ids.map((id, i) => {
|
|
43
|
-
const stringId = id.toString()
|
|
44
|
-
const allFound = graph[i].some((obj: any) => {
|
|
45
|
-
const neighborId = obj.id
|
|
46
|
-
const neighborIndex = parseInt(neighborId.split(',')[0])
|
|
47
|
-
return graph[neighborIndex].some((obj: any) => obj.id === stringId)
|
|
48
|
-
})
|
|
49
|
-
return allFound
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
console.log(bidirectional)
|
|
53
|
-
console.log(graph)
|