@streamr/dht 100.0.0-testnet-three.5 → 100.0.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 (174) hide show
  1. package/README.md +1 -1
  2. package/dist/package.json +14 -9
  3. package/dist/src/connection/ConnectionLockHandler.d.ts +2 -2
  4. package/dist/src/connection/ConnectionLockHandler.js +13 -5
  5. package/dist/src/connection/ConnectionLockHandler.js.map +1 -1
  6. package/dist/src/connection/ConnectionManager.d.ts +6 -5
  7. package/dist/src/connection/ConnectionManager.js +16 -8
  8. package/dist/src/connection/ConnectionManager.js.map +1 -1
  9. package/dist/src/connection/ConnectorFacade.js.map +1 -1
  10. package/dist/src/connection/Handshaker.d.ts +2 -0
  11. package/dist/src/connection/Handshaker.js +12 -14
  12. package/dist/src/connection/Handshaker.js.map +1 -1
  13. package/dist/src/connection/ManagedConnection.js +1 -0
  14. package/dist/src/connection/ManagedConnection.js.map +1 -1
  15. package/dist/src/connection/connectivityChecker.d.ts +1 -1
  16. package/dist/src/connection/connectivityChecker.js +5 -5
  17. package/dist/src/connection/connectivityChecker.js.map +1 -1
  18. package/dist/src/connection/connectivityRequestHandler.d.ts +2 -2
  19. package/dist/src/connection/connectivityRequestHandler.js +6 -7
  20. package/dist/src/connection/connectivityRequestHandler.js.map +1 -1
  21. package/dist/src/connection/webrtc/WebrtcConnector.d.ts +1 -0
  22. package/dist/src/connection/webrtc/WebrtcConnector.js +13 -8
  23. package/dist/src/connection/webrtc/WebrtcConnector.js.map +1 -1
  24. package/dist/src/connection/webrtc/WebrtcConnectorRpcLocal.d.ts +2 -0
  25. package/dist/src/connection/webrtc/WebrtcConnectorRpcLocal.js +4 -6
  26. package/dist/src/connection/webrtc/WebrtcConnectorRpcLocal.js.map +1 -1
  27. package/dist/src/connection/websocket/WebsocketConnector.js +24 -21
  28. package/dist/src/connection/websocket/WebsocketConnector.js.map +1 -1
  29. package/dist/src/connection/websocket/WebsocketServer.js +25 -35
  30. package/dist/src/connection/websocket/WebsocketServer.js.map +1 -1
  31. package/dist/src/connection/websocket/{ServerWebsocket.d.ts → WebsocketServerConnection.d.ts} +4 -5
  32. package/dist/src/connection/websocket/{ServerWebsocket.js → WebsocketServerConnection.js} +15 -48
  33. package/dist/src/connection/websocket/WebsocketServerConnection.js.map +1 -0
  34. package/dist/src/dht/DhtNode.d.ts +15 -7
  35. package/dist/src/dht/DhtNode.js +63 -21
  36. package/dist/src/dht/DhtNode.js.map +1 -1
  37. package/dist/src/dht/DhtNodeRpcLocal.d.ts +5 -1
  38. package/dist/src/dht/DhtNodeRpcLocal.js +10 -0
  39. package/dist/src/dht/DhtNodeRpcLocal.js.map +1 -1
  40. package/dist/src/dht/DhtNodeRpcRemote.d.ts +3 -0
  41. package/dist/src/dht/DhtNodeRpcRemote.js +15 -0
  42. package/dist/src/dht/DhtNodeRpcRemote.js.map +1 -1
  43. package/dist/src/dht/ExternalApiRpcLocal.d.ts +2 -2
  44. package/dist/src/dht/ExternalApiRpcLocal.js +3 -3
  45. package/dist/src/dht/ExternalApiRpcLocal.js.map +1 -1
  46. package/dist/src/dht/ExternalApiRpcRemote.d.ts +1 -1
  47. package/dist/src/dht/ExternalApiRpcRemote.js +2 -2
  48. package/dist/src/dht/ExternalApiRpcRemote.js.map +1 -1
  49. package/dist/src/dht/PeerManager.d.ts +12 -0
  50. package/dist/src/dht/PeerManager.js +30 -5
  51. package/dist/src/dht/PeerManager.js.map +1 -1
  52. package/dist/src/dht/contact/RingContactList.d.ts +31 -0
  53. package/dist/src/dht/contact/RingContactList.js +133 -0
  54. package/dist/src/dht/contact/RingContactList.js.map +1 -0
  55. package/dist/src/dht/contact/ringIdentifiers.d.ts +16 -0
  56. package/dist/src/dht/contact/ringIdentifiers.js +54 -0
  57. package/dist/src/dht/contact/ringIdentifiers.js.map +1 -0
  58. package/dist/src/dht/discovery/PeerDiscovery.d.ts +4 -0
  59. package/dist/src/dht/discovery/PeerDiscovery.js +35 -0
  60. package/dist/src/dht/discovery/PeerDiscovery.js.map +1 -1
  61. package/dist/src/dht/discovery/RingDiscoverySession.d.ts +29 -0
  62. package/dist/src/dht/discovery/RingDiscoverySession.js +123 -0
  63. package/dist/src/dht/discovery/RingDiscoverySession.js.map +1 -0
  64. package/dist/src/dht/recursive-operation/RecursiveOperationSession.js +0 -1
  65. package/dist/src/dht/recursive-operation/RecursiveOperationSession.js.map +1 -1
  66. package/dist/src/dht/routing/Router.d.ts +5 -2
  67. package/dist/src/dht/routing/Router.js +17 -3
  68. package/dist/src/dht/routing/Router.js.map +1 -1
  69. package/dist/src/dht/routing/RouterRpcLocal.d.ts +2 -3
  70. package/dist/src/dht/routing/RouterRpcLocal.js +2 -2
  71. package/dist/src/dht/routing/RouterRpcLocal.js.map +1 -1
  72. package/dist/src/dht/routing/RoutingSession.d.ts +8 -6
  73. package/dist/src/dht/routing/RoutingSession.js +42 -40
  74. package/dist/src/dht/routing/RoutingSession.js.map +1 -1
  75. package/dist/src/dht/routing/RoutingTablesCache.d.ts +24 -0
  76. package/dist/src/dht/routing/RoutingTablesCache.js +46 -0
  77. package/dist/src/dht/routing/RoutingTablesCache.js.map +1 -0
  78. package/dist/src/dht/store/StoreManager.js +1 -1
  79. package/dist/src/dht/store/StoreManager.js.map +1 -1
  80. package/dist/src/exports.d.ts +1 -0
  81. package/dist/src/exports.js.map +1 -1
  82. package/dist/src/helpers/createPeerDescriptor.d.ts +1 -1
  83. package/dist/src/helpers/createPeerDescriptor.js +2 -1
  84. package/dist/src/helpers/createPeerDescriptor.js.map +1 -1
  85. package/dist/src/helpers/version.d.ts +6 -0
  86. package/dist/src/helpers/version.js +38 -0
  87. package/dist/src/helpers/version.js.map +1 -0
  88. package/dist/src/proto/packages/dht/protos/DhtRpc.client.d.ts +16 -6
  89. package/dist/src/proto/packages/dht/protos/DhtRpc.client.js +11 -4
  90. package/dist/src/proto/packages/dht/protos/DhtRpc.client.js.map +1 -1
  91. package/dist/src/proto/packages/dht/protos/DhtRpc.d.ts +98 -87
  92. package/dist/src/proto/packages/dht/protos/DhtRpc.js +45 -49
  93. package/dist/src/proto/packages/dht/protos/DhtRpc.js.map +1 -1
  94. package/dist/src/proto/packages/dht/protos/DhtRpc.server.d.ts +10 -4
  95. package/dist/src/transport/RoutingRpcCommunicator.js +0 -2
  96. package/dist/src/transport/RoutingRpcCommunicator.js.map +1 -1
  97. package/package.json +14 -9
  98. package/protos/DhtRpc.proto +21 -21
  99. package/src/connection/ConnectionLockHandler.ts +13 -5
  100. package/src/connection/ConnectionManager.ts +20 -12
  101. package/src/connection/ConnectorFacade.ts +0 -1
  102. package/src/connection/Handshaker.ts +14 -17
  103. package/src/connection/ManagedConnection.ts +1 -0
  104. package/src/connection/connectivityChecker.ts +6 -7
  105. package/src/connection/connectivityRequestHandler.ts +12 -12
  106. package/src/connection/webrtc/NodeWebrtcConnection.ts +1 -1
  107. package/src/connection/webrtc/WebrtcConnector.ts +14 -8
  108. package/src/connection/webrtc/WebrtcConnectorRpcLocal.ts +5 -5
  109. package/src/connection/websocket/WebsocketConnector.ts +25 -26
  110. package/src/connection/websocket/WebsocketServer.ts +27 -42
  111. package/src/connection/websocket/{ServerWebsocket.ts → WebsocketServerConnection.ts} +14 -55
  112. package/src/dht/DhtNode.ts +85 -40
  113. package/src/dht/DhtNodeRpcLocal.ts +16 -0
  114. package/src/dht/DhtNodeRpcRemote.ts +18 -0
  115. package/src/dht/ExternalApiRpcLocal.ts +5 -5
  116. package/src/dht/ExternalApiRpcRemote.ts +4 -4
  117. package/src/dht/PeerManager.ts +48 -12
  118. package/src/dht/contact/RingContactList.ts +151 -0
  119. package/src/dht/contact/ringIdentifiers.ts +62 -0
  120. package/src/dht/discovery/PeerDiscovery.ts +37 -0
  121. package/src/dht/discovery/RingDiscoverySession.ts +160 -0
  122. package/src/dht/recursive-operation/RecursiveOperationSession.ts +1 -3
  123. package/src/dht/routing/Router.ts +22 -6
  124. package/src/dht/routing/RouterRpcLocal.ts +4 -5
  125. package/src/dht/routing/RoutingSession.ts +50 -44
  126. package/src/dht/routing/RoutingTablesCache.ts +58 -0
  127. package/src/dht/store/StoreManager.ts +1 -1
  128. package/src/exports.ts +1 -0
  129. package/src/helpers/createPeerDescriptor.ts +2 -1
  130. package/src/helpers/version.ts +32 -0
  131. package/src/proto/packages/dht/protos/DhtRpc.client.ts +22 -9
  132. package/src/proto/packages/dht/protos/DhtRpc.server.ts +10 -4
  133. package/src/proto/packages/dht/protos/DhtRpc.ts +122 -100
  134. package/src/transport/RoutingRpcCommunicator.ts +1 -2
  135. package/test/benchmark/Find.test.ts +3 -4
  136. package/test/benchmark/KademliaCorrectness.test.ts +14 -8
  137. package/test/benchmark/RingCorrectness.test.ts +157 -0
  138. package/test/benchmark/hybrid-network-simulation/RingContactList.test.ts +72 -0
  139. package/test/data/generateGroundTruthData.ts +2 -2
  140. package/test/end-to-end/memory-leak.test.ts +1 -2
  141. package/test/integration/ConnectionManager.test.ts +28 -10
  142. package/test/integration/ConnectivityChecking.test.ts +3 -15
  143. package/test/integration/DhtNodeExternalAPI.test.ts +6 -6
  144. package/test/integration/Find.test.ts +4 -4
  145. package/test/integration/Layer1-scale.test.ts +0 -1
  146. package/test/integration/ReplicateData.test.ts +1 -1
  147. package/test/integration/RouteMessage.test.ts +1 -6
  148. package/test/integration/RouterRpcRemote.test.ts +1 -3
  149. package/test/integration/SimultaneousConnections.test.ts +9 -10
  150. package/test/integration/Store.test.ts +2 -2
  151. package/test/integration/StoreAndDelete.test.ts +3 -3
  152. package/test/integration/StoreOnDhtWithThreeNodes.test.ts +5 -5
  153. package/test/integration/StoreOnDhtWithTwoNodes.test.ts +3 -3
  154. package/test/integration/WebrtcConnectionManagement.test.ts +3 -9
  155. package/test/integration/WebsocketConnectionManagement.test.ts +1 -6
  156. package/test/integration/rpc-connections-over-webrpc.test.ts +1 -2
  157. package/test/unit/PeerManager.test.ts +5 -2
  158. package/test/unit/RecursiveOperationManager.test.ts +14 -8
  159. package/test/unit/RecursiveOperationSession.test.ts +1 -1
  160. package/test/unit/Router.test.ts +2 -3
  161. package/test/unit/RoutingSession.test.ts +8 -3
  162. package/test/unit/connectivityRequestHandler.test.ts +5 -9
  163. package/test/unit/createPeerDescriptor.test.ts +12 -6
  164. package/test/unit/version.test.ts +18 -0
  165. package/test/utils/mock/Router.ts +9 -0
  166. package/test/utils/utils.ts +43 -10
  167. package/tsconfig.jest.json +2 -1
  168. package/tsconfig.node.json +2 -1
  169. package/dist/src/connection/websocket/ServerWebsocket.js.map +0 -1
  170. package/dist/src/helpers/versionCompatibility.d.ts +0 -2
  171. package/dist/src/helpers/versionCompatibility.js +0 -18
  172. package/dist/src/helpers/versionCompatibility.js.map +0 -1
  173. package/src/helpers/versionCompatibility.ts +0 -13
  174. package/test/unit/versionCompatibility.test.ts +0 -16
@@ -13,29 +13,30 @@ import { EXISTING_CONNECTION_TIMEOUT } from '../contact/RpcRemote'
13
13
  import { getPreviousPeer } from './getPreviousPeer'
14
14
  import { DhtAddress, areEqualPeerDescriptors, getDhtAddressFromRaw, getNodeIdFromPeerDescriptor } from '../../identifiers'
15
15
  import { pull } from 'lodash'
16
+ import { RoutingTable, RoutingTablesCache } from './RoutingTablesCache'
16
17
 
17
18
  const logger = new Logger(module)
18
19
 
19
20
  const MAX_FAILED_HOPS = 2
20
- const CONTACT_LIST_MAX_SIZE = 10
21
+ const ROUTING_TABLE_MAX_SIZE = 20
21
22
 
22
- class RemoteContact extends Contact {
23
+ export class RoutingRemoteContact extends Contact {
23
24
 
24
25
  private routerRpcRemote: RouterRpcRemote
25
26
  private recursiveOperationRpcRemote: RecursiveOperationRpcRemote
26
27
 
27
- constructor(peer: DhtNodeRpcRemote, localPeerDescriptor: PeerDescriptor, rpcCommunicator: RoutingRpcCommunicator) {
28
- super(peer.getPeerDescriptor())
28
+ constructor(peer: PeerDescriptor, localPeerDescriptor: PeerDescriptor, rpcCommunicator: RoutingRpcCommunicator) {
29
+ super(peer)
29
30
  this.routerRpcRemote = new RouterRpcRemote(
30
31
  localPeerDescriptor,
31
- peer.getPeerDescriptor(),
32
+ peer,
32
33
  rpcCommunicator,
33
34
  RouterRpcClient,
34
35
  EXISTING_CONNECTION_TIMEOUT
35
36
  )
36
37
  this.recursiveOperationRpcRemote = new RecursiveOperationRpcRemote(
37
38
  localPeerDescriptor,
38
- peer.getPeerDescriptor(),
39
+ peer,
39
40
  rpcCommunicator,
40
41
  RecursiveOperationRpcClient,
41
42
  EXISTING_CONNECTION_TIMEOUT
@@ -71,14 +72,15 @@ interface RoutingSessionConfig {
71
72
  connections: Map<DhtAddress, DhtNodeRpcRemote>
72
73
  parallelism: number
73
74
  mode: RoutingMode
74
- excludedNodeIds?: Set<DhtAddress>
75
+ excludedNodeIds: Set<DhtAddress>
76
+ routingTablesCache: RoutingTablesCache
75
77
  }
76
78
 
77
79
  export class RoutingSession extends EventEmitter<RoutingSessionEvents> {
78
80
 
79
81
  public readonly sessionId = v4()
80
82
  private ongoingRequests: Set<DhtAddress> = new Set()
81
- private contactList: SortedContactList<RemoteContact>
83
+ private contactedPeers: Set<DhtAddress> = new Set()
82
84
  private failedHopCounter = 0
83
85
  private successfulHopCounter = 0
84
86
  private stopped = false
@@ -87,16 +89,6 @@ export class RoutingSession extends EventEmitter<RoutingSessionEvents> {
87
89
  constructor(config: RoutingSessionConfig) {
88
90
  super()
89
91
  this.config = config
90
- const previousPeer = getPreviousPeer(config.routedMessage)
91
- const previousId = previousPeer ? getNodeIdFromPeerDescriptor(previousPeer) : undefined
92
- this.contactList = new SortedContactList({
93
- referenceId: getDhtAddressFromRaw(config.routedMessage.target),
94
- maxSize: CONTACT_LIST_MAX_SIZE,
95
- allowToContainReferenceId: true,
96
- nodeIdDistanceLimit: previousId,
97
- excludedNodeIds: config.excludedNodeIds,
98
- emitEvents: false
99
- })
100
92
  }
101
93
 
102
94
  private onRequestFailed(nodeId: DhtAddress) {
@@ -108,14 +100,17 @@ export class RoutingSession extends EventEmitter<RoutingSessionEvents> {
108
100
  this.ongoingRequests.delete(nodeId)
109
101
  }
110
102
  this.deleteParallelRootIfSource(nodeId)
103
+ this.failedHopCounter += 1
104
+ if (this.failedHopCounter >= MAX_FAILED_HOPS) {
105
+ logger.trace(`Stopping routing after ${MAX_FAILED_HOPS} failed attempts for sessionId: ${this.sessionId}`)
106
+ this.emitFailure()
107
+ return
108
+ }
111
109
  const contacts = this.updateAndGetRoutablePeers()
112
110
  if (contacts.length === 0 && this.ongoingRequests.size === 0) {
113
111
  logger.trace('routing failed, emitting routingFailed sessionId: ' + this.sessionId)
114
- // TODO should call this.stop() so that we do cleanup? (after the emitFailure call)
115
- this.stopped = true
116
112
  this.emitFailure()
117
113
  } else {
118
- this.failedHopCounter += 1
119
114
  logger.trace('routing failed, retrying to route sessionId: ' + this.sessionId + ' failedHopCounter: ' + this.failedHopCounter)
120
115
  this.sendMoreRequests(contacts)
121
116
  }
@@ -135,17 +130,19 @@ export class RoutingSession extends EventEmitter<RoutingSessionEvents> {
135
130
  return
136
131
  }
137
132
  this.successfulHopCounter += 1
133
+ if (this.successfulHopCounter >= this.config.parallelism) {
134
+ this.emit('routingSucceeded')
135
+ return
136
+ }
138
137
  const contacts = this.updateAndGetRoutablePeers()
139
- if (this.successfulHopCounter >= this.config.parallelism || contacts.length === 0) {
140
- // TODO should call this.stop() so that we do cleanup? (after the routingSucceeded call)
141
- this.stopped = true
138
+ if (contacts.length === 0) {
142
139
  this.emit('routingSucceeded')
143
140
  } else if (contacts.length > 0 && this.ongoingRequests.size === 0) {
144
141
  this.sendMoreRequests(contacts)
145
142
  }
146
143
  }
147
144
 
148
- private async sendRouteMessageRequest(contact: RemoteContact): Promise<boolean> {
145
+ private async sendRouteMessageRequest(contact: RoutingRemoteContact): Promise<boolean> {
149
146
  if (this.stopped) {
150
147
  return false
151
148
  }
@@ -162,21 +159,36 @@ export class RoutingSession extends EventEmitter<RoutingSessionEvents> {
162
159
  }
163
160
  }
164
161
 
165
- updateAndGetRoutablePeers(): RemoteContact[] {
162
+ updateAndGetRoutablePeers(): RoutingRemoteContact[] {
166
163
  logger.trace('getRoutablePeers() sessionId: ' + this.sessionId)
167
- // Remove stale contacts that may have been removed from connections
168
- this.contactList.getContactIds().forEach((nodeId) => {
169
- if (!this.config.connections.has(nodeId)) {
170
- this.contactList.removeContact(nodeId)
171
- }
172
- })
173
- const contacts = Array.from(this.config.connections.values())
174
- .map((peer) => new RemoteContact(peer, this.config.localPeerDescriptor, this.config.rpcCommunicator))
175
- this.contactList.addContacts(contacts)
176
- return this.contactList.getUncontactedContacts(this.config.parallelism)
164
+ const previousPeer = getPreviousPeer(this.config.routedMessage)
165
+ const previousId = previousPeer ? getNodeIdFromPeerDescriptor(previousPeer) : undefined
166
+ const targetId = getDhtAddressFromRaw(this.config.routedMessage.target)
167
+ let routingTable: RoutingTable
168
+ if (this.config.routingTablesCache.has(targetId, previousId)) {
169
+ routingTable = this.config.routingTablesCache.get(targetId, previousId)!
170
+ } else {
171
+ routingTable = new SortedContactList<RoutingRemoteContact>({
172
+ referenceId: getDhtAddressFromRaw(this.config.routedMessage.target),
173
+ maxSize: ROUTING_TABLE_MAX_SIZE,
174
+ allowToContainReferenceId: true,
175
+ nodeIdDistanceLimit: previousId,
176
+ emitEvents: false
177
+ })
178
+ const contacts = Array.from(this.config.connections.values())
179
+ .map((peer) => new RoutingRemoteContact(
180
+ peer.getPeerDescriptor(),
181
+ this.config.localPeerDescriptor,
182
+ this.config.rpcCommunicator
183
+ ))
184
+ routingTable.addContacts(contacts)
185
+ this.config.routingTablesCache.set(targetId, routingTable, previousId)
186
+ }
187
+ return routingTable.getAllContacts()
188
+ .filter((contact) => !this.contactedPeers.has(contact.getNodeId()) && !this.config.excludedNodeIds.has(contact.getNodeId()))
177
189
  }
178
190
 
179
- sendMoreRequests(uncontacted: RemoteContact[]): void {
191
+ sendMoreRequests(uncontacted: RoutingRemoteContact[]): void {
180
192
  logger.trace('sendMoreRequests() sessionId: ' + this.sessionId)
181
193
  if (this.stopped) {
182
194
  return
@@ -185,16 +197,11 @@ export class RoutingSession extends EventEmitter<RoutingSessionEvents> {
185
197
  this.emitFailure()
186
198
  return
187
199
  }
188
- if (this.failedHopCounter >= MAX_FAILED_HOPS) {
189
- logger.trace(`Stopping routing after ${MAX_FAILED_HOPS} failed attempts for sessionId: ${this.sessionId}`)
190
- this.emitFailure()
191
- return
192
- }
193
200
  while ((this.ongoingRequests.size < this.config.parallelism) && (uncontacted.length > 0) && !this.stopped) {
194
201
  const nextPeer = uncontacted.shift()
195
202
  // eslint-disable-next-line max-len
196
203
  logger.trace(`Sending routeMessage request to contact: ${getNodeIdFromPeerDescriptor(nextPeer!.getPeerDescriptor())} (sessionId=${this.sessionId})`)
197
- this.contactList.setContacted(nextPeer!.getNodeId())
204
+ this.contactedPeers.add(nextPeer!.getNodeId())
198
205
  this.ongoingRequests.add(nextPeer!.getNodeId())
199
206
  this.addParallelRootIfSource(nextPeer!.getNodeId())
200
207
  setImmediate(async () => {
@@ -234,7 +241,6 @@ export class RoutingSession extends EventEmitter<RoutingSessionEvents> {
234
241
 
235
242
  public stop(): void {
236
243
  this.stopped = true
237
- this.contactList.stop()
238
244
  this.emit('stopped')
239
245
  this.removeAllListeners()
240
246
  }
@@ -0,0 +1,58 @@
1
+ import { DhtAddress } from '../../identifiers'
2
+ import { SortedContactList } from '../contact/SortedContactList'
3
+ import { RoutingRemoteContact } from './RoutingSession'
4
+ import { LRUCache } from 'lru-cache'
5
+
6
+ type RoutingTableID = string
7
+ export type RoutingTable = Pick<SortedContactList<RoutingRemoteContact>, 'getAllContacts' | 'addContacts' | 'addContact' | 'removeContact' | 'stop'>
8
+
9
+ const createRoutingTableId = (targetId: DhtAddress, previousId?: DhtAddress): RoutingTableID => {
10
+ return targetId + (previousId ? previousId : '')
11
+ }
12
+
13
+ const DEFAULT_LRU_OPTIONS = {
14
+ max: 1000,
15
+ maxAge: 15 * 1000
16
+ }
17
+
18
+ /**
19
+ * RoutingTablesCache is a cache for routing tables.
20
+ * It is used to store the routing tables for a specific targetId and previousId.
21
+ * Storing the previousId is important as it is used as a minimum distance for the contacts in the table.
22
+ * Calculating a RoutingTable from scratch is an O(n log n) operation (n = number of connections of a node)
23
+ * However,
24
+ * - Adding a contact to a RoutingTable is an O(log n) operation.
25
+ * - Deleting a contact from a RoutingTable is an O(1) operation.
26
+ * Thus, holding the most frequently used routing tables in memory to be updated on
27
+ * connections and disconnections is hugely beneficial in terms of performance.
28
+ */
29
+
30
+ export class RoutingTablesCache {
31
+
32
+ private readonly tables: LRUCache<RoutingTableID, RoutingTable> = new LRUCache(DEFAULT_LRU_OPTIONS)
33
+
34
+ get(targetId: DhtAddress, previousId?: DhtAddress): RoutingTable | undefined {
35
+ return this.tables.get(createRoutingTableId(targetId, previousId))
36
+ }
37
+
38
+ set(targetId: DhtAddress, table: RoutingTable, previousId?: DhtAddress): void {
39
+ this.tables.set(createRoutingTableId(targetId, previousId), table)
40
+ }
41
+
42
+ has(targetId: DhtAddress, previousId?: DhtAddress): boolean {
43
+ return this.tables.has(createRoutingTableId(targetId, previousId))
44
+ }
45
+
46
+ onNodeDisconnected(nodeId: DhtAddress): void {
47
+ this.tables.forEach((table) => table.removeContact(nodeId))
48
+ }
49
+
50
+ onNodeConnected(remote: RoutingRemoteContact): void {
51
+ this.tables.forEach((table) => table.addContact(remote))
52
+ }
53
+
54
+ reset(): void {
55
+ this.tables.forEach((table) => table.stop())
56
+ this.tables.clear()
57
+ }
58
+ }
@@ -98,7 +98,7 @@ export class StoreManager {
98
98
 
99
99
  public async storeDataToDht(key: DhtAddress, data: Any, creator: DhtAddress): Promise<PeerDescriptor[]> {
100
100
  logger.debug(`Storing data to DHT ${this.config.serviceId}`)
101
- const result = await this.config.recursiveOperationManager.execute(key, RecursiveOperation.FIND_NODE)
101
+ const result = await this.config.recursiveOperationManager.execute(key, RecursiveOperation.FIND_CLOSEST_NODES)
102
102
  const closestNodes = result.closestNodes
103
103
  const successfulNodes: PeerDescriptor[] = []
104
104
  const ttl = this.config.highestTtl // ToDo: make TTL decrease according to some nice curve
package/src/exports.ts CHANGED
@@ -17,6 +17,7 @@ export { ClientWebsocket } from './connection/websocket/ClientWebsocket'
17
17
  export { ManagedConnection } from './connection/ManagedConnection'
18
18
  export { ConnectionType } from './connection/IConnection'
19
19
  export { ServiceID } from './types/ServiceID'
20
+ export { RingContacts } from './dht/contact/RingContactList'
20
21
  export {
21
22
  DhtAddress,
22
23
  DhtAddressRaw,
@@ -29,7 +29,7 @@ const calculateNodeIdRaw = (ipAddress: number, privateKey: Uint8Array): DhtAddre
29
29
  return nodeIdRaw
30
30
  }
31
31
 
32
- export const createPeerDescriptor = (connectivityResponse: ConnectivityResponse, nodeId?: DhtAddress): PeerDescriptor => {
32
+ export const createPeerDescriptor = (connectivityResponse: ConnectivityResponse, region: number, nodeId?: DhtAddress): PeerDescriptor => {
33
33
  const privateKey = crypto.randomBytes(32)
34
34
  const publicKey = crypto.randomBytes(20) // TODO calculate publicKey from privateKey
35
35
  let nodeIdRaw: DhtAddressRaw
@@ -42,6 +42,7 @@ export const createPeerDescriptor = (connectivityResponse: ConnectivityResponse,
42
42
  nodeId: nodeIdRaw,
43
43
  type: isBrowserEnvironment() ? NodeType.BROWSER : NodeType.NODEJS,
44
44
  ipAddress: connectivityResponse.ipAddress,
45
+ region,
45
46
  publicKey
46
47
  }
47
48
  if (connectivityResponse.websocket) {
@@ -0,0 +1,32 @@
1
+ export const LOCAL_PROTOCOL_VERSION = '1.0'
2
+
3
+ /*
4
+ * When two nodes negotiate whether they are compatible or not, it is up to the
5
+ * newer version to decide if it supports the old version or not.
6
+ *
7
+ * The older version assumes optimistically that it may be supported by the newer
8
+ * version. It can't know for sure, but the other node will tell if it is not
9
+ * supported (e.g. rejecting the handshake with UNSUPPORTED_VERSION error).
10
+ */
11
+ export const isMaybeSupportedVersion = (remoteVersion: string): boolean => {
12
+ const localMajor = parseVersion(LOCAL_PROTOCOL_VERSION)!.major
13
+ const remoteMajor = parseVersion(remoteVersion)?.major
14
+ if ((remoteMajor === undefined) || (remoteMajor < localMajor)) {
15
+ return false
16
+ } else {
17
+ // TODO implement proper checking when there are new protocol versions
18
+ return true
19
+ }
20
+ }
21
+
22
+ export const parseVersion = (version: string): { major: number, minor: number } | undefined => {
23
+ const parts = version.split('.')
24
+ if (parts.length === 2) {
25
+ const values = parts.map((p) => Number(p))
26
+ if (!values.some((v) => isNaN(v))) {
27
+ return { major: values[0], minor: values[1] }
28
+ }
29
+ } else {
30
+ return undefined
31
+ }
32
+ }
@@ -4,8 +4,8 @@
4
4
  import { ExternalApiRpc } from "./DhtRpc";
5
5
  import type { ExternalStoreDataResponse } from "./DhtRpc";
6
6
  import type { ExternalStoreDataRequest } from "./DhtRpc";
7
- import type { ExternalFindDataResponse } from "./DhtRpc";
8
- import type { ExternalFindDataRequest } from "./DhtRpc";
7
+ import type { ExternalFetchDataResponse } from "./DhtRpc";
8
+ import type { ExternalFetchDataRequest } from "./DhtRpc";
9
9
  import { ConnectionLockRpc } from "./DhtRpc";
10
10
  import type { DisconnectNotice } from "./DhtRpc";
11
11
  import type { UnlockRequest } from "./DhtRpc";
@@ -35,6 +35,8 @@ import type { Empty } from "../../../google/protobuf/empty";
35
35
  import type { LeaveNotice } from "./DhtRpc";
36
36
  import type { PingResponse } from "./DhtRpc";
37
37
  import type { PingRequest } from "./DhtRpc";
38
+ import type { ClosestRingPeersResponse } from "./DhtRpc";
39
+ import type { ClosestRingPeersRequest } from "./DhtRpc";
38
40
  import { stackIntercept } from "@protobuf-ts/runtime-rpc";
39
41
  import type { ClosestPeersResponse } from "./DhtRpc";
40
42
  import type { ClosestPeersRequest } from "./DhtRpc";
@@ -48,6 +50,10 @@ export interface IDhtNodeRpcClient {
48
50
  * @generated from protobuf rpc: getClosestPeers(dht.ClosestPeersRequest) returns (dht.ClosestPeersResponse);
49
51
  */
50
52
  getClosestPeers(input: ClosestPeersRequest, options?: RpcOptions): UnaryCall<ClosestPeersRequest, ClosestPeersResponse>;
53
+ /**
54
+ * @generated from protobuf rpc: getClosestRingPeers(dht.ClosestRingPeersRequest) returns (dht.ClosestRingPeersResponse);
55
+ */
56
+ getClosestRingPeers(input: ClosestRingPeersRequest, options?: RpcOptions): UnaryCall<ClosestRingPeersRequest, ClosestRingPeersResponse>;
51
57
  /**
52
58
  * @generated from protobuf rpc: ping(dht.PingRequest) returns (dht.PingResponse);
53
59
  */
@@ -73,18 +79,25 @@ export class DhtNodeRpcClient implements IDhtNodeRpcClient, ServiceInfo {
73
79
  const method = this.methods[0], opt = this._transport.mergeOptions(options);
74
80
  return stackIntercept<ClosestPeersRequest, ClosestPeersResponse>("unary", this._transport, method, opt, input);
75
81
  }
82
+ /**
83
+ * @generated from protobuf rpc: getClosestRingPeers(dht.ClosestRingPeersRequest) returns (dht.ClosestRingPeersResponse);
84
+ */
85
+ getClosestRingPeers(input: ClosestRingPeersRequest, options?: RpcOptions): UnaryCall<ClosestRingPeersRequest, ClosestRingPeersResponse> {
86
+ const method = this.methods[1], opt = this._transport.mergeOptions(options);
87
+ return stackIntercept<ClosestRingPeersRequest, ClosestRingPeersResponse>("unary", this._transport, method, opt, input);
88
+ }
76
89
  /**
77
90
  * @generated from protobuf rpc: ping(dht.PingRequest) returns (dht.PingResponse);
78
91
  */
79
92
  ping(input: PingRequest, options?: RpcOptions): UnaryCall<PingRequest, PingResponse> {
80
- const method = this.methods[1], opt = this._transport.mergeOptions(options);
93
+ const method = this.methods[2], opt = this._transport.mergeOptions(options);
81
94
  return stackIntercept<PingRequest, PingResponse>("unary", this._transport, method, opt, input);
82
95
  }
83
96
  /**
84
97
  * @generated from protobuf rpc: leaveNotice(dht.LeaveNotice) returns (google.protobuf.Empty);
85
98
  */
86
99
  leaveNotice(input: LeaveNotice, options?: RpcOptions): UnaryCall<LeaveNotice, Empty> {
87
- const method = this.methods[2], opt = this._transport.mergeOptions(options);
100
+ const method = this.methods[3], opt = this._transport.mergeOptions(options);
88
101
  return stackIntercept<LeaveNotice, Empty>("unary", this._transport, method, opt, input);
89
102
  }
90
103
  }
@@ -352,9 +365,9 @@ export class ConnectionLockRpcClient implements IConnectionLockRpcClient, Servic
352
365
  */
353
366
  export interface IExternalApiRpcClient {
354
367
  /**
355
- * @generated from protobuf rpc: externalFindData(dht.ExternalFindDataRequest) returns (dht.ExternalFindDataResponse);
368
+ * @generated from protobuf rpc: externalFetchData(dht.ExternalFetchDataRequest) returns (dht.ExternalFetchDataResponse);
356
369
  */
357
- externalFindData(input: ExternalFindDataRequest, options?: RpcOptions): UnaryCall<ExternalFindDataRequest, ExternalFindDataResponse>;
370
+ externalFetchData(input: ExternalFetchDataRequest, options?: RpcOptions): UnaryCall<ExternalFetchDataRequest, ExternalFetchDataResponse>;
358
371
  /**
359
372
  * @generated from protobuf rpc: externalStoreData(dht.ExternalStoreDataRequest) returns (dht.ExternalStoreDataResponse);
360
373
  */
@@ -370,11 +383,11 @@ export class ExternalApiRpcClient implements IExternalApiRpcClient, ServiceInfo
370
383
  constructor(private readonly _transport: RpcTransport) {
371
384
  }
372
385
  /**
373
- * @generated from protobuf rpc: externalFindData(dht.ExternalFindDataRequest) returns (dht.ExternalFindDataResponse);
386
+ * @generated from protobuf rpc: externalFetchData(dht.ExternalFetchDataRequest) returns (dht.ExternalFetchDataResponse);
374
387
  */
375
- externalFindData(input: ExternalFindDataRequest, options?: RpcOptions): UnaryCall<ExternalFindDataRequest, ExternalFindDataResponse> {
388
+ externalFetchData(input: ExternalFetchDataRequest, options?: RpcOptions): UnaryCall<ExternalFetchDataRequest, ExternalFetchDataResponse> {
376
389
  const method = this.methods[0], opt = this._transport.mergeOptions(options);
377
- return stackIntercept<ExternalFindDataRequest, ExternalFindDataResponse>("unary", this._transport, method, opt, input);
390
+ return stackIntercept<ExternalFetchDataRequest, ExternalFetchDataResponse>("unary", this._transport, method, opt, input);
378
391
  }
379
392
  /**
380
393
  * @generated from protobuf rpc: externalStoreData(dht.ExternalStoreDataRequest) returns (dht.ExternalStoreDataResponse);
@@ -3,8 +3,8 @@
3
3
  // tslint:disable
4
4
  import { ExternalStoreDataResponse } from "./DhtRpc";
5
5
  import { ExternalStoreDataRequest } from "./DhtRpc";
6
- import { ExternalFindDataResponse } from "./DhtRpc";
7
- import { ExternalFindDataRequest } from "./DhtRpc";
6
+ import { ExternalFetchDataResponse } from "./DhtRpc";
7
+ import { ExternalFetchDataRequest } from "./DhtRpc";
8
8
  import { DisconnectNotice } from "./DhtRpc";
9
9
  import { UnlockRequest } from "./DhtRpc";
10
10
  import { LockResponse } from "./DhtRpc";
@@ -24,6 +24,8 @@ import { Empty } from "../../../google/protobuf/empty";
24
24
  import { LeaveNotice } from "./DhtRpc";
25
25
  import { PingResponse } from "./DhtRpc";
26
26
  import { PingRequest } from "./DhtRpc";
27
+ import { ClosestRingPeersResponse } from "./DhtRpc";
28
+ import { ClosestRingPeersRequest } from "./DhtRpc";
27
29
  import { ClosestPeersResponse } from "./DhtRpc";
28
30
  import { ClosestPeersRequest } from "./DhtRpc";
29
31
  import { ServerCallContext } from "@protobuf-ts/runtime-rpc";
@@ -35,6 +37,10 @@ export interface IDhtNodeRpc<T = ServerCallContext> {
35
37
  * @generated from protobuf rpc: getClosestPeers(dht.ClosestPeersRequest) returns (dht.ClosestPeersResponse);
36
38
  */
37
39
  getClosestPeers(request: ClosestPeersRequest, context: T): Promise<ClosestPeersResponse>;
40
+ /**
41
+ * @generated from protobuf rpc: getClosestRingPeers(dht.ClosestRingPeersRequest) returns (dht.ClosestRingPeersResponse);
42
+ */
43
+ getClosestRingPeers(request: ClosestRingPeersRequest, context: T): Promise<ClosestRingPeersResponse>;
38
44
  /**
39
45
  * @generated from protobuf rpc: ping(dht.PingRequest) returns (dht.PingResponse);
40
46
  */
@@ -140,9 +146,9 @@ export interface IConnectionLockRpc<T = ServerCallContext> {
140
146
  */
141
147
  export interface IExternalApiRpc<T = ServerCallContext> {
142
148
  /**
143
- * @generated from protobuf rpc: externalFindData(dht.ExternalFindDataRequest) returns (dht.ExternalFindDataResponse);
149
+ * @generated from protobuf rpc: externalFetchData(dht.ExternalFetchDataRequest) returns (dht.ExternalFetchDataResponse);
144
150
  */
145
- externalFindData(request: ExternalFindDataRequest, context: T): Promise<ExternalFindDataResponse>;
151
+ externalFetchData(request: ExternalFetchDataRequest, context: T): Promise<ExternalFetchDataResponse>;
146
152
  /**
147
153
  * @generated from protobuf rpc: externalStoreData(dht.ExternalStoreDataRequest) returns (dht.ExternalStoreDataResponse);
148
154
  */