@streamr/dht 100.2.4-beta.0 → 100.2.5-beta.0

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.
Files changed (159) hide show
  1. package/dist/package.json +8 -9
  2. package/dist/src/connection/ConnectionManager.d.ts +2 -1
  3. package/dist/src/connection/ConnectionManager.js +2 -10
  4. package/dist/src/connection/ConnectionManager.js.map +1 -1
  5. package/dist/src/connection/ConnectionsView.d.ts +7 -0
  6. package/dist/src/connection/ConnectionsView.js +3 -0
  7. package/dist/src/connection/ConnectionsView.js.map +1 -0
  8. package/dist/src/connection/ConnectorFacade.d.ts +2 -1
  9. package/dist/src/connection/ConnectorFacade.js +5 -4
  10. package/dist/src/connection/ConnectorFacade.js.map +1 -1
  11. package/dist/src/connection/connectivityChecker.d.ts +3 -3
  12. package/dist/src/connection/connectivityChecker.js +3 -3
  13. package/dist/src/connection/connectivityChecker.js.map +1 -1
  14. package/dist/src/connection/connectivityRequestHandler.d.ts +2 -1
  15. package/dist/src/connection/connectivityRequestHandler.js +13 -6
  16. package/dist/src/connection/connectivityRequestHandler.js.map +1 -1
  17. package/dist/src/connection/{ManagedWebrtcConnection.d.ts → webrtc/ManagedWebrtcConnection.d.ts} +3 -3
  18. package/dist/src/connection/{ManagedWebrtcConnection.js → webrtc/ManagedWebrtcConnection.js} +2 -2
  19. package/dist/src/connection/webrtc/ManagedWebrtcConnection.js.map +1 -0
  20. package/dist/src/connection/webrtc/NodeWebrtcConnection.js +3 -26
  21. package/dist/src/connection/webrtc/NodeWebrtcConnection.js.map +1 -1
  22. package/dist/src/connection/webrtc/WebrtcConnector.js +1 -1
  23. package/dist/src/connection/webrtc/WebrtcConnector.js.map +1 -1
  24. package/dist/src/connection/webrtc/WebrtcConnectorRpcLocal.d.ts +1 -1
  25. package/dist/src/connection/webrtc/WebrtcConnectorRpcLocal.js +1 -1
  26. package/dist/src/connection/webrtc/WebrtcConnectorRpcLocal.js.map +1 -1
  27. package/dist/src/connection/websocket/AbstractWebsocketClientConnection.d.ts +1 -1
  28. package/dist/src/connection/websocket/WebsocketConnector.d.ts +3 -1
  29. package/dist/src/connection/websocket/WebsocketConnector.js +26 -18
  30. package/dist/src/connection/websocket/WebsocketConnector.js.map +1 -1
  31. package/dist/src/connection/websocket/WebsocketServerConnection.d.ts +2 -1
  32. package/dist/src/connection/websocket/WebsocketServerConnection.js +4 -0
  33. package/dist/src/connection/websocket/WebsocketServerConnection.js.map +1 -1
  34. package/dist/src/dht/DhtNode.d.ts +14 -11
  35. package/dist/src/dht/DhtNode.js +74 -61
  36. package/dist/src/dht/DhtNode.js.map +1 -1
  37. package/dist/src/dht/DhtNodeRpcLocal.d.ts +3 -3
  38. package/dist/src/dht/DhtNodeRpcLocal.js +5 -2
  39. package/dist/src/dht/DhtNodeRpcLocal.js.map +1 -1
  40. package/dist/src/dht/PeerManager.d.ts +14 -14
  41. package/dist/src/dht/PeerManager.js +51 -42
  42. package/dist/src/dht/PeerManager.js.map +1 -1
  43. package/dist/src/dht/contact/ContactList.d.ts +1 -2
  44. package/dist/src/dht/contact/ContactList.js +1 -3
  45. package/dist/src/dht/contact/ContactList.js.map +1 -1
  46. package/dist/src/dht/contact/RandomContactList.d.ts +1 -1
  47. package/dist/src/dht/contact/RandomContactList.js +7 -4
  48. package/dist/src/dht/contact/RandomContactList.js.map +1 -1
  49. package/dist/src/dht/contact/getClosestNodes.d.ts +6 -0
  50. package/dist/src/dht/contact/{getClosestContacts.js → getClosestNodes.js} +7 -6
  51. package/dist/src/dht/contact/getClosestNodes.js.map +1 -0
  52. package/dist/src/dht/discovery/DiscoverySession.d.ts +4 -0
  53. package/dist/src/dht/discovery/DiscoverySession.js +27 -21
  54. package/dist/src/dht/discovery/DiscoverySession.js.map +1 -1
  55. package/dist/src/dht/discovery/PeerDiscovery.d.ts +8 -5
  56. package/dist/src/dht/discovery/PeerDiscovery.js +34 -24
  57. package/dist/src/dht/discovery/PeerDiscovery.js.map +1 -1
  58. package/dist/src/dht/discovery/RingDiscoverySession.d.ts +4 -4
  59. package/dist/src/dht/discovery/RingDiscoverySession.js +10 -19
  60. package/dist/src/dht/discovery/RingDiscoverySession.js.map +1 -1
  61. package/dist/src/dht/recursive-operation/RecursiveOperationManager.d.ts +2 -0
  62. package/dist/src/dht/recursive-operation/RecursiveOperationManager.js +3 -3
  63. package/dist/src/dht/recursive-operation/RecursiveOperationManager.js.map +1 -1
  64. package/dist/src/dht/store/StoreManager.d.ts +5 -6
  65. package/dist/src/dht/store/StoreManager.js +21 -76
  66. package/dist/src/dht/store/StoreManager.js.map +1 -1
  67. package/dist/src/dht/store/StoreRpcLocal.d.ts +5 -2
  68. package/dist/src/dht/store/StoreRpcLocal.js +22 -5
  69. package/dist/src/dht/store/StoreRpcLocal.js.map +1 -1
  70. package/dist/src/exports.d.ts +2 -1
  71. package/dist/src/exports.js.map +1 -1
  72. package/dist/src/helpers/version.d.ts +1 -1
  73. package/dist/src/helpers/version.js +1 -1
  74. package/dist/src/proto/google/protobuf/any.d.ts +8 -5
  75. package/dist/src/proto/google/protobuf/any.js.map +1 -1
  76. package/dist/src/proto/google/protobuf/empty.d.ts +0 -1
  77. package/dist/src/proto/google/protobuf/empty.js.map +1 -1
  78. package/dist/src/proto/google/protobuf/timestamp.d.ts +10 -1
  79. package/dist/src/proto/google/protobuf/timestamp.js.map +1 -1
  80. package/dist/src/proto/packages/dht/protos/DhtRpc.d.ts +10 -2
  81. package/dist/src/proto/packages/dht/protos/DhtRpc.js +4 -2
  82. package/dist/src/proto/packages/dht/protos/DhtRpc.js.map +1 -1
  83. package/dist/src/transport/ITransport.d.ts +0 -4
  84. package/dist/src/transport/ITransport.js.map +1 -1
  85. package/karma.config.js +2 -2
  86. package/package.json +8 -9
  87. package/protos/DhtRpc.proto +3 -1
  88. package/src/connection/ConnectionManager.ts +4 -10
  89. package/src/connection/ConnectionsView.ts +8 -0
  90. package/src/connection/ConnectorFacade.ts +7 -5
  91. package/src/connection/connectivityChecker.ts +4 -4
  92. package/src/connection/connectivityRequestHandler.ts +19 -6
  93. package/src/connection/{ManagedWebrtcConnection.ts → webrtc/ManagedWebrtcConnection.ts} +4 -4
  94. package/src/connection/webrtc/NodeWebrtcConnection.ts +3 -3
  95. package/src/connection/webrtc/WebrtcConnector.ts +1 -1
  96. package/src/connection/webrtc/WebrtcConnectorRpcLocal.ts +3 -3
  97. package/src/connection/websocket/AbstractWebsocketClientConnection.ts +1 -1
  98. package/src/connection/websocket/WebsocketConnector.ts +33 -21
  99. package/src/connection/websocket/WebsocketServerConnection.ts +6 -1
  100. package/src/dht/DhtNode.ts +102 -74
  101. package/src/dht/DhtNodeRpcLocal.ts +12 -5
  102. package/src/dht/PeerManager.ts +58 -49
  103. package/src/dht/contact/ContactList.ts +1 -4
  104. package/src/dht/contact/RandomContactList.ts +7 -5
  105. package/src/dht/contact/{getClosestContacts.ts → getClosestNodes.ts} +8 -6
  106. package/src/dht/discovery/DiscoverySession.ts +34 -22
  107. package/src/dht/discovery/PeerDiscovery.ts +44 -30
  108. package/src/dht/discovery/RingDiscoverySession.ts +15 -29
  109. package/src/dht/recursive-operation/RecursiveOperationManager.ts +5 -3
  110. package/src/dht/store/StoreManager.ts +46 -84
  111. package/src/dht/store/StoreRpcLocal.ts +32 -9
  112. package/src/exports.ts +2 -1
  113. package/src/helpers/version.ts +1 -1
  114. package/src/proto/google/protobuf/any.ts +8 -5
  115. package/src/proto/google/protobuf/empty.ts +0 -1
  116. package/src/proto/google/protobuf/timestamp.ts +10 -1
  117. package/src/proto/packages/dht/protos/DhtRpc.ts +14 -4
  118. package/src/transport/ITransport.ts +0 -4
  119. package/test/benchmark/Find.test.ts +1 -1
  120. package/test/benchmark/WebsocketServerMemoryLeak.test.ts +1 -1
  121. package/test/end-to-end/GeoIpLayer0.test.ts +55 -0
  122. package/test/end-to-end/Layer0-Layer1.test.ts +4 -4
  123. package/test/end-to-end/Layer0Webrtc-Layer1.test.ts +11 -5
  124. package/test/end-to-end/Layer1-Scale-WebSocket.test.ts +7 -1
  125. package/test/end-to-end/Layer1-Scale-Webrtc.test.ts +11 -2
  126. package/test/integration/ConnectionLocking.test.ts +1 -1
  127. package/test/integration/ConnectionManager.test.ts +3 -3
  128. package/test/integration/ConnectivityChecking.test.ts +3 -3
  129. package/test/integration/DhtNode.test.ts +5 -20
  130. package/test/integration/GeoIpConnectivityChecking.test.ts +71 -0
  131. package/test/integration/Layer1-scale.test.ts +6 -6
  132. package/test/integration/RouteMessage.test.ts +4 -0
  133. package/test/integration/ScaleDownDht.test.ts +1 -1
  134. package/test/integration/SimultaneousConnections.test.ts +2 -2
  135. package/test/integration/WebrtcConnectionManagement.test.ts +1 -1
  136. package/test/integration/Websocket.test.ts +1 -1
  137. package/test/integration/WebsocketConnectionManagement.test.ts +1 -1
  138. package/test/integration/rpc-connections-over-webrpc.test.ts +1 -1
  139. package/test/unit/AutoCertifierClientFacade.test.ts +1 -1
  140. package/test/unit/DiscoverySession.test.ts +4 -2
  141. package/test/unit/PeerManager.test.ts +45 -51
  142. package/test/unit/RandomContactList.test.ts +10 -0
  143. package/test/unit/RecursiveOperationManager.test.ts +4 -2
  144. package/test/unit/StoreManager.test.ts +42 -34
  145. package/test/unit/StoreRpcLocal.test.ts +167 -0
  146. package/test/unit/WebsocketConnector.test.ts +1 -1
  147. package/test/unit/connectivityRequestHandler.test.ts +3 -3
  148. package/test/unit/getClosestNodes.test.ts +30 -0
  149. package/test/utils/FakeTransport.ts +4 -2
  150. package/test/utils/mock/MockConnectionsView.ts +18 -0
  151. package/test/utils/mock/{Transport.ts → MockTransport.ts} +0 -15
  152. package/test/utils/utils.ts +4 -1
  153. package/tsconfig.jest.json +2 -1
  154. package/tsconfig.node.json +2 -1
  155. package/dist/src/connection/ManagedWebrtcConnection.js.map +0 -1
  156. package/dist/src/dht/contact/getClosestContacts.d.ts +0 -7
  157. package/dist/src/dht/contact/getClosestContacts.js.map +0 -1
  158. package/test/unit/getClosestContacts.test.ts +0 -28
  159. /package/test/utils/mock/{Router.ts → MockRouter.ts} +0 -0
@@ -1,19 +1,14 @@
1
- import { Logger, runAndWaitForEvents3 } from '@streamr/utils'
2
- import EventEmitter from 'eventemitter3'
1
+ import { Gate, Logger, withTimeout } from '@streamr/utils'
3
2
  import { v4 } from 'uuid'
3
+ import { DhtAddress, getNodeIdFromPeerDescriptor } from '../../identifiers'
4
4
  import { PeerDescriptor } from '../../proto/packages/dht/protos/DhtRpc'
5
- import { PeerManager } from '../PeerManager'
6
5
  import { DhtNodeRpcRemote } from '../DhtNodeRpcRemote'
7
- import { DhtAddress, getNodeIdFromPeerDescriptor } from '../../identifiers'
8
- import { RingId, RingIdRaw, getLeftDistance, getRingIdFromPeerDescriptor, getRingIdFromRaw } from '../contact/ringIdentifiers'
6
+ import { PeerManager } from '../PeerManager'
9
7
  import { RingContacts } from '../contact/RingContactList'
8
+ import { RingId, RingIdRaw, getLeftDistance, getRingIdFromPeerDescriptor, getRingIdFromRaw } from '../contact/ringIdentifiers'
10
9
 
11
10
  const logger = new Logger(module)
12
11
 
13
- interface RingDiscoverySessionEvents {
14
- discoveryCompleted: () => void
15
- }
16
-
17
12
  interface RingDiscoverySessionConfig {
18
13
  targetId: RingIdRaw
19
14
  parallelism: number
@@ -21,15 +16,15 @@ interface RingDiscoverySessionConfig {
21
16
  peerManager: PeerManager
22
17
  // Note that contacted peers will be mutated by the DiscoverySession or other parallel sessions
23
18
  contactedPeers: Set<DhtAddress>
19
+ abortSignal: AbortSignal
24
20
  }
25
21
 
26
22
  export class RingDiscoverySession {
27
23
 
28
24
  public readonly id = v4()
29
- private stopped = false
30
- private emitter = new EventEmitter<RingDiscoverySessionEvents>()
31
25
  private noProgressCounter = 0
32
26
  private ongoingRequests: Set<DhtAddress> = new Set()
27
+ private doneGate = new Gate(false)
33
28
  private readonly config: RingDiscoverySessionConfig
34
29
  private numContactedPeers = 0
35
30
  private targetIdAsRingId: RingId
@@ -40,7 +35,7 @@ export class RingDiscoverySession {
40
35
  }
41
36
 
42
37
  private addContacts(contacts: PeerDescriptor[]): void {
43
- if (this.stopped) {
38
+ if (this.config.abortSignal.aborted || this.doneGate.isOpen()) {
44
39
  return
45
40
  }
46
41
  for (const contact of contacts) {
@@ -49,7 +44,7 @@ export class RingDiscoverySession {
49
44
  }
50
45
 
51
46
  private async fetchClosestContactsFromRemote(contact: DhtNodeRpcRemote): Promise<RingContacts> {
52
- if (this.stopped) {
47
+ if (this.config.abortSignal.aborted || this.doneGate.isOpen()) {
53
48
  return { left: [], right: [] }
54
49
  }
55
50
  logger.trace(`Getting closest ring peers from contact: ${getNodeIdFromPeerDescriptor(contact.getPeerDescriptor())}`)
@@ -94,7 +89,7 @@ export class RingDiscoverySession {
94
89
  }
95
90
 
96
91
  private findMoreContacts(): void {
97
- if (this.stopped) {
92
+ if (this.config.abortSignal.aborted || this.doneGate.isOpen()) {
98
93
  return
99
94
  }
100
95
  const uncontacted = this.config.peerManager.getClosestRingContactsTo(
@@ -104,8 +99,7 @@ export class RingDiscoverySession {
104
99
  )
105
100
  if ((uncontacted.left.length === 0 && uncontacted.right.length === 0)
106
101
  || this.noProgressCounter >= this.config.noProgressLimit) {
107
- this.emitter.emit('discoveryCompleted')
108
- this.stopped = true
102
+ this.doneGate.open()
109
103
  return
110
104
  }
111
105
  // ask from both sides equally
@@ -143,20 +137,12 @@ export class RingDiscoverySession {
143
137
  }
144
138
 
145
139
  public async findClosestNodes(timeout: number): Promise<void> {
146
- if (this.config.peerManager.getContactCount(this.config.contactedPeers) === 0) {
140
+ if (this.config.peerManager.getNearbyContactCount(this.config.contactedPeers) === 0) {
147
141
  return
148
142
  }
149
- // TODO add abortController and signal it in stop()
150
- await runAndWaitForEvents3<RingDiscoverySessionEvents>(
151
- [this.findMoreContacts.bind(this)],
152
- [[this.emitter, 'discoveryCompleted']],
153
- timeout
154
- )
155
- }
156
-
157
- public stop(): void {
158
- this.stopped = true
159
- this.emitter.emit('discoveryCompleted')
160
- this.emitter.removeAllListeners()
143
+ setImmediate(() => {
144
+ this.findMoreContacts()
145
+ })
146
+ await withTimeout(this.doneGate.waitUntilOpen(), timeout, 'discovery session timed out', this.config.abortSignal)
161
147
  }
162
148
  }
@@ -25,11 +25,13 @@ import { ServiceID } from '../../types/ServiceID'
25
25
  import { RecursiveOperationRpcLocal } from './RecursiveOperationRpcLocal'
26
26
  import { DhtAddress, areEqualPeerDescriptors, getDhtAddressFromRaw, getNodeIdFromPeerDescriptor, getRawFromDhtAddress } from '../../identifiers'
27
27
  import { getDistance } from '../PeerManager'
28
+ import { ConnectionsView } from '../../exports'
28
29
 
29
30
  interface RecursiveOperationManagerConfig {
30
31
  rpcCommunicator: RoutingRpcCommunicator
31
32
  sessionTransport: ITransport
32
33
  router: Router
34
+ connectionsView: ConnectionsView
33
35
  localPeerDescriptor: PeerDescriptor
34
36
  serviceId: ServiceID
35
37
  localDataStore: LocalDataStore
@@ -87,14 +89,14 @@ export class RecursiveOperationManager {
87
89
  targetId,
88
90
  localPeerDescriptor: this.config.localPeerDescriptor,
89
91
  // TODO use config option or named constant?
90
- waitedRoutingPathCompletions: this.config.sessionTransport.getConnectionCount() > 1 ? 2 : 1,
92
+ waitedRoutingPathCompletions: this.config.connectionsView.getConnectionCount() > 1 ? 2 : 1,
91
93
  operation,
92
94
  // TODO would it make sense to give excludedPeer as one of the fields RecursiveOperationSession?
93
95
  doRouteRequest: (routedMessage: RouteMessageWrapper) => {
94
96
  return this.doRouteRequest(routedMessage, excludedPeer)
95
97
  }
96
98
  })
97
- if (this.config.sessionTransport.getConnectionCount() === 0) {
99
+ if (this.config.connectionsView.getConnectionCount() === 0) {
98
100
  const dataEntries = Array.from(this.config.localDataStore.values(targetId))
99
101
  session.onResponseReceived(
100
102
  getNodeIdFromPeerDescriptor(this.config.localPeerDescriptor),
@@ -218,7 +220,7 @@ export class RecursiveOperationManager {
218
220
  }
219
221
 
220
222
  private getClosestConnectedNodes(referenceId: DhtAddress, limit: number): PeerDescriptor[] {
221
- const connectedNodes = this.config.sessionTransport.getConnections().map((c) => this.config.createDhtNodeRpcRemote(c))
223
+ const connectedNodes = this.config.connectionsView.getConnections().map((c) => this.config.createDhtNodeRpcRemote(c))
222
224
  const sorted = new SortedContactList<DhtNodeRpcRemote>({
223
225
  referenceId,
224
226
  maxSize: limit,
@@ -1,21 +1,28 @@
1
+ import { ServerCallContext } from '@protobuf-ts/runtime-rpc'
2
+ import { Logger } from '@streamr/utils'
1
3
  import {
2
- DataEntry, ReplicateDataRequest, PeerDescriptor,
3
- StoreDataRequest, StoreDataResponse, RecursiveOperation
4
- } from '../../proto/packages/dht/protos/DhtRpc'
4
+ DhtAddress,
5
+ areEqualPeerDescriptors,
6
+ getDhtAddressFromRaw,
7
+ getNodeIdFromPeerDescriptor,
8
+ getRawFromDhtAddress
9
+ } from '../../identifiers'
5
10
  import { Any } from '../../proto/google/protobuf/any'
6
- import { ServerCallContext } from '@protobuf-ts/runtime-rpc'
11
+ import { Timestamp } from '../../proto/google/protobuf/timestamp'
12
+ import {
13
+ DataEntry,
14
+ PeerDescriptor,
15
+ RecursiveOperation,
16
+ ReplicateDataRequest,
17
+ StoreDataRequest, StoreDataResponse
18
+ } from '../../proto/packages/dht/protos/DhtRpc'
7
19
  import { RoutingRpcCommunicator } from '../../transport/RoutingRpcCommunicator'
20
+ import { ServiceID } from '../../types/ServiceID'
21
+ import { getClosestNodes } from '../contact/getClosestNodes'
8
22
  import { RecursiveOperationManager } from '../recursive-operation/RecursiveOperationManager'
9
- import { Logger, executeSafePromise } from '@streamr/utils'
10
23
  import { LocalDataStore } from './LocalDataStore'
11
- import { StoreRpcRemote } from './StoreRpcRemote'
12
- import { Timestamp } from '../../proto/google/protobuf/timestamp'
13
- import { SortedContactList } from '../contact/SortedContactList'
14
- import { Contact } from '../contact/Contact'
15
- import { ServiceID } from '../../types/ServiceID'
16
- import { DhtAddress, areEqualPeerDescriptors, getDhtAddressFromRaw, getNodeIdFromPeerDescriptor, getRawFromDhtAddress } from '../../identifiers'
17
24
  import { StoreRpcLocal } from './StoreRpcLocal'
18
- import { getDistance } from '../PeerManager'
25
+ import { StoreRpcRemote } from './StoreRpcRemote'
19
26
 
20
27
  interface StoreManagerConfig {
21
28
  rpcCommunicator: RoutingRpcCommunicator
@@ -25,7 +32,7 @@ interface StoreManagerConfig {
25
32
  serviceId: ServiceID
26
33
  highestTtl: number
27
34
  redundancyFactor: number
28
- getClosestNeighborsTo: (dataKey: DhtAddress, n?: number) => PeerDescriptor[]
35
+ getNeighbors: () => ReadonlyArray<PeerDescriptor>
29
36
  createRpcRemote: (contact: PeerDescriptor) => StoreRpcRemote
30
37
  }
31
38
 
@@ -42,9 +49,10 @@ export class StoreManager {
42
49
 
43
50
  private registerLocalRpcMethods() {
44
51
  const rpcLocal = new StoreRpcLocal({
52
+ localPeerDescriptor: this.config.localPeerDescriptor,
45
53
  localDataStore: this.config.localDataStore,
46
- replicateDataToNeighbors: (incomingPeer: PeerDescriptor, dataEntry: DataEntry) => this.replicateDataToNeighbors(incomingPeer, dataEntry),
47
- selfIsWithinRedundancyFactor: (key: DhtAddress): boolean => this.selfIsWithinRedundancyFactor(key)
54
+ replicateDataToContact: (dataEntry: DataEntry, contact: PeerDescriptor) => this.replicateDataToContact(dataEntry, contact),
55
+ getStorers: (dataKey: DhtAddress) => this.getStorers(dataKey)
48
56
  })
49
57
  this.config.rpcCommunicator.registerRpcMethod(StoreDataRequest, StoreDataResponse, 'storeData',
50
58
  (request: StoreDataRequest) => rpcLocal.storeData(request))
@@ -58,31 +66,19 @@ export class StoreManager {
58
66
  }
59
67
  }
60
68
 
61
- private replicateAndUpdateStaleState(key: DhtAddress, newNode: PeerDescriptor): void {
62
- const newNodeId = getNodeIdFromPeerDescriptor(newNode)
63
- const closestToData = this.config.getClosestNeighborsTo(key, this.config.redundancyFactor)
64
- const sortedList = new SortedContactList<Contact>({
65
- referenceId: key,
66
- maxSize: this.config.redundancyFactor,
67
- allowToContainReferenceId: true
68
- })
69
- sortedList.addContact(new Contact(this.config.localPeerDescriptor))
70
- closestToData.forEach((neighbor) => {
71
- if (newNodeId !== getNodeIdFromPeerDescriptor(neighbor)) {
72
- sortedList.addContact(new Contact(neighbor))
73
- }
74
- })
75
- const selfIsPrimaryStorer = (sortedList.getClosestContactId() === getNodeIdFromPeerDescriptor(this.config.localPeerDescriptor))
76
- if (selfIsPrimaryStorer) {
77
- sortedList.addContact(new Contact(newNode))
78
- if (sortedList.getContact(newNodeId) !== undefined) {
69
+ private replicateAndUpdateStaleState(dataKey: DhtAddress, newNode: PeerDescriptor): void {
70
+ const storers = this.getStorers(dataKey)
71
+ const storersBeforeContactAdded = storers.filter((p) => !areEqualPeerDescriptors(p, newNode))
72
+ const selfWasPrimaryStorer = areEqualPeerDescriptors(storersBeforeContactAdded[0], this.config.localPeerDescriptor)
73
+ if (selfWasPrimaryStorer) {
74
+ if (storers.some((p) => areEqualPeerDescriptors(p, newNode))) {
79
75
  setImmediate(async () => {
80
- const dataEntries = Array.from(this.config.localDataStore.values(key))
76
+ const dataEntries = Array.from(this.config.localDataStore.values(dataKey))
81
77
  await Promise.all(dataEntries.map(async (dataEntry) => this.replicateDataToContact(dataEntry, newNode)))
82
78
  })
83
79
  }
84
- } else if (!this.selfIsWithinRedundancyFactor(key)) {
85
- this.config.localDataStore.setAllEntriesAsStale(key)
80
+ } else if (!storers.some((p) => areEqualPeerDescriptors(p, this.config.localPeerDescriptor))) {
81
+ this.config.localDataStore.setAllEntriesAsStale(dataKey)
86
82
  }
87
83
  }
88
84
 
@@ -137,21 +133,15 @@ export class StoreManager {
137
133
  return successfulNodes
138
134
  }
139
135
 
140
- private selfIsWithinRedundancyFactor(dataKey: DhtAddress): boolean {
141
- const closestNeighbors = this.config.getClosestNeighborsTo(dataKey, this.config.redundancyFactor)
142
- if (closestNeighbors.length < this.config.redundancyFactor) {
143
- return true
144
- } else {
145
- const furthestCloseNeighbor = closestNeighbors[closestNeighbors.length - 1]
146
- const dataKeyRaw = getRawFromDhtAddress(dataKey)
147
- return getDistance(dataKeyRaw, this.config.localPeerDescriptor.nodeId) < getDistance(dataKeyRaw, furthestCloseNeighbor.nodeId)
148
- }
149
- }
150
-
151
136
  private async replicateDataToClosestNodes(): Promise<void> {
152
137
  const dataEntries = Array.from(this.config.localDataStore.values())
153
138
  await Promise.all(dataEntries.map(async (dataEntry) => {
154
- const neighbors = this.config.getClosestNeighborsTo(getDhtAddressFromRaw(dataEntry.key), this.config.redundancyFactor)
139
+ const dataKey = getDhtAddressFromRaw(dataEntry.key)
140
+ const neighbors = getClosestNodes(
141
+ dataKey,
142
+ this.config.getNeighbors(),
143
+ { maxCount: this.config.redundancyFactor }
144
+ )
155
145
  await Promise.all(neighbors.map(async (neighbor) => {
156
146
  const rpcRemote = this.config.createRpcRemote(neighbor)
157
147
  try {
@@ -163,43 +153,15 @@ export class StoreManager {
163
153
  }))
164
154
  }
165
155
 
166
- private replicateDataToNeighbors(incomingPeer: PeerDescriptor, dataEntry: DataEntry): void {
167
- // sort own contact list according to data id
168
- const localNodeId = getNodeIdFromPeerDescriptor(this.config.localPeerDescriptor)
169
- const incomingNodeId = getNodeIdFromPeerDescriptor(incomingPeer)
170
- const key = getDhtAddressFromRaw(dataEntry.key)
171
- // TODO use config option or named constant?
172
- const closestToData = this.config.getClosestNeighborsTo(key, 10)
173
- const sortedList = new SortedContactList<Contact>({
174
- referenceId: key,
175
- maxSize: this.config.redundancyFactor,
176
- allowToContainReferenceId: true
177
- })
178
- sortedList.addContact(new Contact(this.config.localPeerDescriptor))
179
- closestToData.forEach((neighbor) => {
180
- sortedList.addContact(new Contact(neighbor))
181
- })
182
- const selfIsPrimaryStorer = (sortedList.getClosestContactId() === localNodeId)
183
- const targetLimit = selfIsPrimaryStorer
184
- // if we are the closest to the data, replicate to all storageRedundancyFactor nearest
185
- ? undefined
186
- // if we are not the closest node to the data, replicate only to the closest one to the data
187
- : 1
188
- const targets = sortedList.getClosestContacts(targetLimit)
189
- targets.forEach((contact) => {
190
- const contactNodeId = contact.getNodeId()
191
- if ((incomingNodeId !== contactNodeId) && (localNodeId !== contactNodeId)) {
192
- setImmediate(() => {
193
- executeSafePromise(async () => {
194
- await this.replicateDataToContact(dataEntry, contact.getPeerDescriptor())
195
- logger.trace('replicateDataToContact() returned', {
196
- node: contactNodeId,
197
- replicateOnlyToClosest: !selfIsPrimaryStorer
198
- })
199
- })
200
- })
156
+ private getStorers(dataKey: DhtAddress, excludedNode?: PeerDescriptor): PeerDescriptor[] {
157
+ return getClosestNodes(
158
+ dataKey,
159
+ [...this.config.getNeighbors(), this.config.localPeerDescriptor],
160
+ {
161
+ maxCount: this.config.redundancyFactor,
162
+ excludedNodeIds: excludedNode !== undefined ? new Set([getNodeIdFromPeerDescriptor(excludedNode)]) : undefined
201
163
  }
202
- })
164
+ )
203
165
  }
204
166
 
205
167
  async destroy(): Promise<void> {
@@ -1,5 +1,5 @@
1
1
  import { ServerCallContext } from '@protobuf-ts/runtime-rpc'
2
- import { Logger } from '@streamr/utils'
2
+ import { Logger, executeSafePromise } from '@streamr/utils'
3
3
  import { Empty } from '../../proto/google/protobuf/empty'
4
4
  import { Timestamp } from '../../proto/google/protobuf/timestamp'
5
5
  import {
@@ -11,12 +11,13 @@ import {
11
11
  import { IStoreRpc } from '../../proto/packages/dht/protos/DhtRpc.server'
12
12
  import { DhtCallContext } from '../../rpc-protocol/DhtCallContext'
13
13
  import { LocalDataStore } from './LocalDataStore'
14
- import { DhtAddress, getDhtAddressFromRaw } from '../../identifiers'
14
+ import { areEqualPeerDescriptors, DhtAddress, getDhtAddressFromRaw } from '../../identifiers'
15
15
 
16
16
  interface StoreRpcLocalConfig {
17
17
  localDataStore: LocalDataStore
18
- replicateDataToNeighbors: (incomingPeer: PeerDescriptor, dataEntry: DataEntry) => void
19
- selfIsWithinRedundancyFactor: (key: DhtAddress) => boolean
18
+ localPeerDescriptor: PeerDescriptor
19
+ replicateDataToContact: (dataEntry: DataEntry, contact: PeerDescriptor) => Promise<void>
20
+ getStorers: (key: DhtAddress) => ReadonlyArray<PeerDescriptor>
20
21
  }
21
22
 
22
23
  const logger = new Logger(module)
@@ -32,7 +33,7 @@ export class StoreRpcLocal implements IStoreRpc {
32
33
  async storeData(request: StoreDataRequest): Promise<StoreDataResponse> {
33
34
  logger.trace('storeData()')
34
35
  const key = getDhtAddressFromRaw(request.key)
35
- const selfIsWithinRedundancyFactor = this.config.selfIsWithinRedundancyFactor(key)
36
+ const isLocalNodeStorer = this.isLocalNodeStorer(key)
36
37
  this.config.localDataStore.storeEntry({
37
38
  key: request.key,
38
39
  data: request.data,
@@ -40,10 +41,10 @@ export class StoreRpcLocal implements IStoreRpc {
40
41
  createdAt: request.createdAt,
41
42
  storedAt: Timestamp.now(),
42
43
  ttl: request.ttl,
43
- stale: !selfIsWithinRedundancyFactor,
44
+ stale: !isLocalNodeStorer,
44
45
  deleted: false
45
46
  })
46
- if (!selfIsWithinRedundancyFactor) {
47
+ if (!isLocalNodeStorer) {
47
48
  this.config.localDataStore.setAllEntriesAsStale(key)
48
49
  }
49
50
  return {}
@@ -54,13 +55,35 @@ export class StoreRpcLocal implements IStoreRpc {
54
55
  const dataEntry = request.entry!
55
56
  const wasStored = this.config.localDataStore.storeEntry(dataEntry)
56
57
  if (wasStored) {
57
- this.config.replicateDataToNeighbors((context as DhtCallContext).incomingSourceDescriptor!, request.entry!)
58
+ this.replicateDataToNeighbors((context as DhtCallContext).incomingSourceDescriptor!, request.entry!)
58
59
  }
59
60
  const key = getDhtAddressFromRaw(dataEntry.key)
60
- if (!this.config.selfIsWithinRedundancyFactor(key)) {
61
+ if (!this.isLocalNodeStorer(key)) {
61
62
  this.config.localDataStore.setAllEntriesAsStale(key)
62
63
  }
63
64
  logger.trace('server-side replicateData() at end')
64
65
  return {}
65
66
  }
67
+
68
+ private isLocalNodeStorer(dataKey: DhtAddress): boolean {
69
+ return this.config.getStorers(dataKey).some((p) => areEqualPeerDescriptors(p, this.config.localPeerDescriptor))
70
+ }
71
+
72
+ private replicateDataToNeighbors(requestor: PeerDescriptor, dataEntry: DataEntry): void {
73
+ const dataKey = getDhtAddressFromRaw(dataEntry.key)
74
+ const storers = this.config.getStorers(dataKey)
75
+ const isLocalNodePrimaryStorer = areEqualPeerDescriptors(storers[0], this.config.localPeerDescriptor)
76
+ // If we are the closest to the data, get storageRedundancyFactor - 1 nearest node to the data, and
77
+ // replicate to all those node. Otherwise replicate only to the one closest one. And never replicate
78
+ // to the requestor nor to itself.
79
+ const targets = (isLocalNodePrimaryStorer ? storers : [storers[0]]).filter(
80
+ (p) => !areEqualPeerDescriptors(p, requestor) && !areEqualPeerDescriptors(p, this.config.localPeerDescriptor)
81
+ )
82
+ targets.forEach((target) => {
83
+ setImmediate(() => {
84
+ executeSafePromise(() => this.config.replicateDataToContact(dataEntry, target))
85
+ })
86
+ })
87
+ }
88
+
66
89
  }
package/src/exports.ts CHANGED
@@ -5,8 +5,9 @@ export { Simulator, LatencyType } from './connection/simulator/Simulator'
5
5
  export { SimulatorTransport } from './connection/simulator/SimulatorTransport'
6
6
  export { getRandomRegion, getRegionDelayMatrix } from './connection/simulator/pings'
7
7
  export { PeerDescriptor, Message, NodeType, DataEntry } from './proto/packages/dht/protos/DhtRpc'
8
- export { ITransport } from './transport/ITransport'
8
+ export { ITransport, TransportEvents } from './transport/ITransport'
9
9
  export { ConnectionManager, ConnectionLocker, PortRange, TlsCertificate } from './connection/ConnectionManager'
10
+ export { ConnectionsView } from './connection/ConnectionsView'
10
11
  export { LockID } from './connection/ConnectionLockStates'
11
12
  export { DefaultConnectorFacade } from './connection/ConnectorFacade'
12
13
  export { DhtRpcOptions } from './rpc-protocol/DhtRpcOptions'
@@ -1,4 +1,4 @@
1
- export const LOCAL_PROTOCOL_VERSION = '1.0'
1
+ export const LOCAL_PROTOCOL_VERSION = '1.1'
2
2
 
3
3
  /*
4
4
  * When two nodes negotiate whether they are compatible or not, it is up to the
@@ -74,7 +74,7 @@ import { MessageType } from "@protobuf-ts/runtime";
74
74
  * foo = any.unpack(Foo.class);
75
75
  * }
76
76
  *
77
- * Example 3: Pack and unpack a message in Python.
77
+ * Example 3: Pack and unpack a message in Python.
78
78
  *
79
79
  * foo = Foo(...)
80
80
  * any = Any()
@@ -84,13 +84,16 @@ import { MessageType } from "@protobuf-ts/runtime";
84
84
  * any.Unpack(foo)
85
85
  * ...
86
86
  *
87
- * Example 4: Pack and unpack a message in Go
87
+ * Example 4: Pack and unpack a message in Go
88
88
  *
89
89
  * foo := &pb.Foo{...}
90
- * any, err := ptypes.MarshalAny(foo)
90
+ * any, err := anypb.New(foo)
91
+ * if err != nil {
92
+ * ...
93
+ * }
91
94
  * ...
92
95
  * foo := &pb.Foo{}
93
- * if err := ptypes.UnmarshalAny(any, foo); err != nil {
96
+ * if err := any.UnmarshalTo(foo); err != nil {
94
97
  * ...
95
98
  * }
96
99
  *
@@ -102,7 +105,7 @@ import { MessageType } from "@protobuf-ts/runtime";
102
105
  *
103
106
  *
104
107
  * JSON
105
- * ====
108
+ *
106
109
  * The JSON representation of an `Any` value uses the regular
107
110
  * representation of the deserialized, embedded message, with an
108
111
  * additional field `@type` which contains the type URL. Example:
@@ -49,7 +49,6 @@ import { MessageType } from "@protobuf-ts/runtime";
49
49
  * rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
50
50
  * }
51
51
  *
52
- * The JSON representation for `Empty` is empty JSON object `{}`.
53
52
  *
54
53
  * @generated from protobuf message google.protobuf.Empty
55
54
  */
@@ -98,7 +98,16 @@ import { MessageType } from "@protobuf-ts/runtime";
98
98
  * .setNanos((int) ((millis % 1000) * 1000000)).build();
99
99
  *
100
100
  *
101
- * Example 5: Compute Timestamp from current time in Python.
101
+ * Example 5: Compute Timestamp from Java `Instant.now()`.
102
+ *
103
+ * Instant now = Instant.now();
104
+ *
105
+ * Timestamp timestamp =
106
+ * Timestamp.newBuilder().setSeconds(now.getEpochSecond())
107
+ * .setNanos(now.getNano()).build();
108
+ *
109
+ *
110
+ * Example 6: Compute Timestamp from current time in Python.
102
111
  *
103
112
  * timestamp = Timestamp()
104
113
  * timestamp.GetCurrentTime()
@@ -351,9 +351,9 @@ export interface ConnectivityRequest {
351
351
  */
352
352
  host?: string;
353
353
  /**
354
- * @generated from protobuf field: bool selfSigned = 4;
354
+ * @generated from protobuf field: bool allowSelfSignedCertificate = 4;
355
355
  */
356
- selfSigned: boolean;
356
+ allowSelfSignedCertificate: boolean;
357
357
  }
358
358
  /**
359
359
  * @generated from protobuf message dht.ConnectivityResponse
@@ -379,6 +379,14 @@ export interface ConnectivityResponse {
379
379
  * @generated from protobuf field: string version = 5;
380
380
  */
381
381
  version: string;
382
+ /**
383
+ * @generated from protobuf field: optional double latitude = 6;
384
+ */
385
+ latitude?: number;
386
+ /**
387
+ * @generated from protobuf field: optional double longitude = 7;
388
+ */
389
+ longitude?: number;
382
390
  }
383
391
  /**
384
392
  * @generated from protobuf message dht.HandshakeRequest
@@ -962,7 +970,7 @@ class ConnectivityRequest$Type extends MessageType<ConnectivityRequest> {
962
970
  { no: 1, name: "port", kind: "scalar", T: 13 /*ScalarType.UINT32*/ },
963
971
  { no: 2, name: "tls", kind: "scalar", T: 8 /*ScalarType.BOOL*/ },
964
972
  { no: 3, name: "host", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ },
965
- { no: 4, name: "selfSigned", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }
973
+ { no: 4, name: "allowSelfSignedCertificate", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }
966
974
  ]);
967
975
  }
968
976
  }
@@ -978,7 +986,9 @@ class ConnectivityResponse$Type extends MessageType<ConnectivityResponse> {
978
986
  { no: 2, name: "natType", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
979
987
  { no: 3, name: "websocket", kind: "message", T: () => ConnectivityMethod },
980
988
  { no: 4, name: "ipAddress", kind: "scalar", T: 13 /*ScalarType.UINT32*/ },
981
- { no: 5, name: "version", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
989
+ { no: 5, name: "version", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
990
+ { no: 6, name: "latitude", kind: "scalar", opt: true, T: 1 /*ScalarType.DOUBLE*/ },
991
+ { no: 7, name: "longitude", kind: "scalar", opt: true, T: 1 /*ScalarType.DOUBLE*/ }
982
992
  ]);
983
993
  }
984
994
  }
@@ -1,4 +1,3 @@
1
- import { DhtAddress } from '../identifiers'
2
1
  import { Message, PeerDescriptor } from '../proto/packages/dht/protos/DhtRpc'
3
2
 
4
3
  export interface TransportEvents {
@@ -33,8 +32,5 @@ export interface ITransport {
33
32
 
34
33
  send(msg: Message, opts?: SendOptions): Promise<void>
35
34
  getLocalPeerDescriptor(): PeerDescriptor
36
- getConnections(): PeerDescriptor[]
37
- getConnectionCount(): number
38
- hasConnection(nodeId: DhtAddress): boolean
39
35
  stop(): void | Promise<void>
40
36
  }
@@ -56,7 +56,7 @@ describe('Find correctness', () => {
56
56
  logger.info('waiting over')
57
57
 
58
58
  nodes.forEach((node) => logger.info(getNodeIdFromPeerDescriptor(node.getLocalPeerDescriptor()) + ': connections:' +
59
- node.getConnectionCount() + ', kbucket: ' + node.getNeighborCount()
59
+ node.getConnectionsView().getConnectionCount() + ', kbucket: ' + node.getNeighborCount()
60
60
  + ', localLocked: ' + node.getLocalLockedConnectionCount()
61
61
  + ', remoteLocked: ' + node.getRemoteLockedConnectionCount()
62
62
  + ', weakLocked: ' + node.getWeakLockedConnectionCount()))
@@ -31,7 +31,7 @@ describe('WebsocketServermemoryLeak', () => {
31
31
  console.log('clientWebsocket connected ' + i)
32
32
  })
33
33
 
34
- clientWebsocket.connect(`ws://127.0.0.1:${port}`)
34
+ clientWebsocket.connect(`ws://127.0.0.1:${port}`, false)
35
35
  i++
36
36
  await wait(3000)
37
37
  }
@@ -0,0 +1,55 @@
1
+ import { WebsocketServerConnection } from '../../src/connection/websocket/WebsocketServerConnection'
2
+ import { DhtNode } from '../../src/dht/DhtNode'
3
+ import fs from 'fs'
4
+
5
+ const WEBSOCKET_PORT_RANGE = { min: 10012, max: 10015 }
6
+
7
+ // '51.120.98.194' is the IP address of norway.no in OSL = 7900
8
+ const testIp = '51.120.98.194'
9
+ const testRegion = 7900
10
+ const dbPath = '/tmp/geoipdatabasesl0'
11
+
12
+ describe('Layer0', () => {
13
+
14
+ let epDhtNode: DhtNode
15
+ let node1: DhtNode
16
+ let mock: jest.SpyInstance<string, [], any>
17
+
18
+ beforeEach(async () => {
19
+
20
+ epDhtNode = new DhtNode({
21
+ websocketHost: '127.0.0.1', websocketPortRange: { min: 10011, max: 10011 }, websocketServerEnableTls: false,
22
+ geoIpDatabaseFolder: dbPath
23
+ })
24
+ await epDhtNode.start()
25
+ await epDhtNode.joinDht([epDhtNode.getLocalPeerDescriptor()])
26
+
27
+ node1 = new DhtNode({
28
+ websocketPortRange: WEBSOCKET_PORT_RANGE,
29
+ websocketHost: '127.0.0.1',
30
+ entryPoints: [epDhtNode.getLocalPeerDescriptor()],
31
+ websocketServerEnableTls: false
32
+ })
33
+
34
+ mock = jest.spyOn(WebsocketServerConnection.prototype, 'getRemoteIpAddress').mockImplementation(() => testIp)
35
+
36
+ }, 10000)
37
+
38
+ afterEach(async () => {
39
+ await Promise.all([
40
+ epDhtNode.stop(),
41
+ node1.stop()
42
+ ])
43
+ fs.unlinkSync(dbPath + '/GeoLite2-City.mmdb')
44
+ fs.rmSync(dbPath, { recursive: true })
45
+ mock.mockRestore()
46
+ })
47
+
48
+ it('Gets the correct region number by IP address', async () => {
49
+ await node1.start()
50
+ await node1.joinDht([epDhtNode.getLocalPeerDescriptor()])
51
+
52
+ expect(node1.getLocalPeerDescriptor().region).toBe(testRegion)
53
+
54
+ }, 10000)
55
+ })
@@ -39,11 +39,11 @@ describe('Layer0-Layer1', () => {
39
39
  await node1.start()
40
40
  await node2.start()
41
41
 
42
- stream1Node1 = new DhtNode({ transport: epDhtNode, serviceId: STREAM_ID1 })
43
- stream1Node2 = new DhtNode({ transport: node1, serviceId: STREAM_ID1 })
42
+ stream1Node1 = new DhtNode({ transport: epDhtNode, connectionsView: epDhtNode.getConnectionsView(), serviceId: STREAM_ID1 })
43
+ stream1Node2 = new DhtNode({ transport: node1, connectionsView: node1.getConnectionsView(), serviceId: STREAM_ID1 })
44
44
 
45
- stream2Node1 = new DhtNode({ transport: epDhtNode, serviceId: STREAM_ID2 })
46
- stream2Node2 = new DhtNode({ transport: node2, serviceId: STREAM_ID2 })
45
+ stream2Node1 = new DhtNode({ transport: epDhtNode, connectionsView: epDhtNode.getConnectionsView(), serviceId: STREAM_ID2 })
46
+ stream2Node2 = new DhtNode({ transport: node2, connectionsView: node2.getConnectionsView(), serviceId: STREAM_ID2 })
47
47
 
48
48
  await Promise.all([
49
49
  stream1Node1.start(),