@streamr/dht 100.0.0-testnet-three.6 → 100.1.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 (215) hide show
  1. package/README.md +1 -1
  2. package/dist/package.json +12 -8
  3. package/dist/src/connection/ConnectionLockRpcLocal.d.ts +1 -1
  4. package/dist/src/connection/ConnectionLockRpcRemote.d.ts +1 -1
  5. package/dist/src/connection/ConnectionLockRpcRemote.js +1 -1
  6. package/dist/src/connection/ConnectionLockRpcRemote.js.map +1 -1
  7. package/dist/src/connection/{ConnectionLockHandler.d.ts → ConnectionLockStates.d.ts} +3 -3
  8. package/dist/src/connection/{ConnectionLockHandler.js → ConnectionLockStates.js} +17 -9
  9. package/dist/src/connection/ConnectionLockStates.js.map +1 -0
  10. package/dist/src/connection/ConnectionManager.d.ts +9 -7
  11. package/dist/src/connection/ConnectionManager.js +16 -18
  12. package/dist/src/connection/ConnectionManager.js.map +1 -1
  13. package/dist/src/connection/ConnectorFacade.js +1 -1
  14. package/dist/src/connection/ConnectorFacade.js.map +1 -1
  15. package/dist/src/connection/Handshaker.js +6 -13
  16. package/dist/src/connection/Handshaker.js.map +1 -1
  17. package/dist/src/connection/ManagedConnection.d.ts +2 -2
  18. package/dist/src/connection/ManagedConnection.js +8 -8
  19. package/dist/src/connection/ManagedConnection.js.map +1 -1
  20. package/dist/src/connection/connectivityChecker.d.ts +1 -1
  21. package/dist/src/connection/connectivityChecker.js +8 -8
  22. package/dist/src/connection/connectivityChecker.js.map +1 -1
  23. package/dist/src/connection/connectivityRequestHandler.d.ts +2 -2
  24. package/dist/src/connection/connectivityRequestHandler.js +10 -11
  25. package/dist/src/connection/connectivityRequestHandler.js.map +1 -1
  26. package/dist/src/connection/webrtc/WebrtcConnector.d.ts +1 -0
  27. package/dist/src/connection/webrtc/WebrtcConnector.js +13 -8
  28. package/dist/src/connection/webrtc/WebrtcConnector.js.map +1 -1
  29. package/dist/src/connection/webrtc/WebrtcConnectorRpcLocal.d.ts +2 -0
  30. package/dist/src/connection/webrtc/WebrtcConnectorRpcLocal.js +4 -6
  31. package/dist/src/connection/webrtc/WebrtcConnectorRpcLocal.js.map +1 -1
  32. package/dist/src/connection/websocket/AbstractWebsocketClientConnection.d.ts +28 -0
  33. package/dist/src/connection/websocket/{ClientWebsocket.js → AbstractWebsocketClientConnection.js} +42 -68
  34. package/dist/src/connection/websocket/AbstractWebsocketClientConnection.js.map +1 -0
  35. package/dist/src/connection/websocket/NodeWebsocketClientConnection.d.ts +7 -0
  36. package/dist/src/connection/websocket/NodeWebsocketClientConnection.js +39 -0
  37. package/dist/src/connection/websocket/NodeWebsocketClientConnection.js.map +1 -0
  38. package/dist/src/connection/websocket/WebsocketConnector.js +26 -23
  39. package/dist/src/connection/websocket/WebsocketConnector.js.map +1 -1
  40. package/dist/src/connection/websocket/WebsocketServer.js +25 -35
  41. package/dist/src/connection/websocket/WebsocketServer.js.map +1 -1
  42. package/dist/src/connection/websocket/{ServerWebsocket.d.ts → WebsocketServerConnection.d.ts} +4 -5
  43. package/dist/src/connection/websocket/{ServerWebsocket.js → WebsocketServerConnection.js} +18 -51
  44. package/dist/src/connection/websocket/WebsocketServerConnection.js.map +1 -0
  45. package/dist/src/dht/DhtNode.d.ts +15 -8
  46. package/dist/src/dht/DhtNode.js +62 -31
  47. package/dist/src/dht/DhtNode.js.map +1 -1
  48. package/dist/src/dht/DhtNodeRpcLocal.d.ts +5 -1
  49. package/dist/src/dht/DhtNodeRpcLocal.js +10 -0
  50. package/dist/src/dht/DhtNodeRpcLocal.js.map +1 -1
  51. package/dist/src/dht/DhtNodeRpcRemote.d.ts +3 -0
  52. package/dist/src/dht/DhtNodeRpcRemote.js +16 -1
  53. package/dist/src/dht/DhtNodeRpcRemote.js.map +1 -1
  54. package/dist/src/dht/ExternalApiRpcLocal.d.ts +2 -2
  55. package/dist/src/dht/ExternalApiRpcLocal.js +3 -3
  56. package/dist/src/dht/ExternalApiRpcLocal.js.map +1 -1
  57. package/dist/src/dht/ExternalApiRpcRemote.d.ts +1 -1
  58. package/dist/src/dht/ExternalApiRpcRemote.js +2 -2
  59. package/dist/src/dht/ExternalApiRpcRemote.js.map +1 -1
  60. package/dist/src/dht/PeerManager.d.ts +15 -3
  61. package/dist/src/dht/PeerManager.js +54 -40
  62. package/dist/src/dht/PeerManager.js.map +1 -1
  63. package/dist/src/dht/contact/RingContactList.d.ts +31 -0
  64. package/dist/src/dht/contact/RingContactList.js +133 -0
  65. package/dist/src/dht/contact/RingContactList.js.map +1 -0
  66. package/dist/src/dht/contact/SortedContactList.d.ts +2 -1
  67. package/dist/src/dht/contact/SortedContactList.js +19 -17
  68. package/dist/src/dht/contact/SortedContactList.js.map +1 -1
  69. package/dist/src/dht/contact/ringIdentifiers.d.ts +16 -0
  70. package/dist/src/dht/contact/ringIdentifiers.js +54 -0
  71. package/dist/src/dht/contact/ringIdentifiers.js.map +1 -0
  72. package/dist/src/dht/discovery/DiscoverySession.js +3 -1
  73. package/dist/src/dht/discovery/DiscoverySession.js.map +1 -1
  74. package/dist/src/dht/discovery/PeerDiscovery.d.ts +6 -2
  75. package/dist/src/dht/discovery/PeerDiscovery.js +41 -4
  76. package/dist/src/dht/discovery/PeerDiscovery.js.map +1 -1
  77. package/dist/src/dht/discovery/RingDiscoverySession.d.ts +29 -0
  78. package/dist/src/dht/discovery/RingDiscoverySession.js +125 -0
  79. package/dist/src/dht/discovery/RingDiscoverySession.js.map +1 -0
  80. package/dist/src/dht/recursive-operation/RecursiveOperationManager.js +1 -1
  81. package/dist/src/dht/recursive-operation/RecursiveOperationManager.js.map +1 -1
  82. package/dist/src/dht/recursive-operation/RecursiveOperationRpcRemote.js +1 -1
  83. package/dist/src/dht/recursive-operation/RecursiveOperationRpcRemote.js.map +1 -1
  84. package/dist/src/dht/recursive-operation/RecursiveOperationSession.js +0 -1
  85. package/dist/src/dht/recursive-operation/RecursiveOperationSession.js.map +1 -1
  86. package/dist/src/dht/routing/Router.d.ts +0 -1
  87. package/dist/src/dht/routing/Router.js +0 -1
  88. package/dist/src/dht/routing/Router.js.map +1 -1
  89. package/dist/src/dht/routing/RouterRpcLocal.d.ts +0 -1
  90. package/dist/src/dht/routing/RouterRpcLocal.js.map +1 -1
  91. package/dist/src/dht/routing/RouterRpcRemote.js +2 -2
  92. package/dist/src/dht/routing/RouterRpcRemote.js.map +1 -1
  93. package/dist/src/dht/routing/RoutingSession.js +2 -2
  94. package/dist/src/dht/routing/RoutingSession.js.map +1 -1
  95. package/dist/src/dht/store/LocalDataStore.js +2 -2
  96. package/dist/src/dht/store/LocalDataStore.js.map +1 -1
  97. package/dist/src/dht/store/StoreManager.js +2 -2
  98. package/dist/src/dht/store/StoreManager.js.map +1 -1
  99. package/dist/src/exports.d.ts +3 -2
  100. package/dist/src/exports.js +3 -3
  101. package/dist/src/exports.js.map +1 -1
  102. package/dist/src/helpers/createPeerDescriptor.d.ts +1 -1
  103. package/dist/src/helpers/createPeerDescriptor.js +2 -1
  104. package/dist/src/helpers/createPeerDescriptor.js.map +1 -1
  105. package/dist/src/helpers/version.d.ts +6 -0
  106. package/dist/src/helpers/version.js +38 -0
  107. package/dist/src/helpers/version.js.map +1 -0
  108. package/dist/src/proto/packages/dht/protos/DhtRpc.client.d.ts +16 -6
  109. package/dist/src/proto/packages/dht/protos/DhtRpc.client.js +11 -4
  110. package/dist/src/proto/packages/dht/protos/DhtRpc.client.js.map +1 -1
  111. package/dist/src/proto/packages/dht/protos/DhtRpc.d.ts +98 -87
  112. package/dist/src/proto/packages/dht/protos/DhtRpc.js +45 -49
  113. package/dist/src/proto/packages/dht/protos/DhtRpc.js.map +1 -1
  114. package/dist/src/proto/packages/dht/protos/DhtRpc.server.d.ts +10 -4
  115. package/dist/src/transport/RoutingRpcCommunicator.js +0 -2
  116. package/dist/src/transport/RoutingRpcCommunicator.js.map +1 -1
  117. package/karma.config.js +4 -1
  118. package/package.json +12 -8
  119. package/protos/DhtRpc.proto +21 -21
  120. package/src/connection/ConnectionLockRpcLocal.ts +1 -1
  121. package/src/connection/ConnectionLockRpcRemote.ts +2 -2
  122. package/src/connection/{ConnectionLockHandler.ts → ConnectionLockStates.ts} +14 -6
  123. package/src/connection/ConnectionManager.ts +21 -22
  124. package/src/connection/ConnectorFacade.ts +1 -2
  125. package/src/connection/Handshaker.ts +7 -15
  126. package/src/connection/ManagedConnection.ts +8 -8
  127. package/src/connection/connectivityChecker.ts +9 -10
  128. package/src/connection/connectivityRequestHandler.ts +16 -16
  129. package/src/connection/webrtc/BrowserWebrtcConnection.ts +18 -0
  130. package/src/connection/webrtc/NodeWebrtcConnection.ts +1 -1
  131. package/src/connection/webrtc/WebrtcConnector.ts +14 -8
  132. package/src/connection/webrtc/WebrtcConnectorRpcLocal.ts +5 -5
  133. package/src/connection/websocket/{ClientWebsocket.ts → AbstractWebsocketClientConnection.ts} +57 -70
  134. package/src/connection/websocket/BrowserWebsocketClientConnection.ts +44 -0
  135. package/src/connection/websocket/NodeWebsocketClientConnection.ts +39 -0
  136. package/src/connection/websocket/WebsocketConnector.ts +27 -28
  137. package/src/connection/websocket/WebsocketServer.ts +27 -42
  138. package/src/connection/websocket/{ServerWebsocket.ts → WebsocketServerConnection.ts} +15 -56
  139. package/src/dht/DhtNode.ts +85 -50
  140. package/src/dht/DhtNodeRpcLocal.ts +16 -0
  141. package/src/dht/DhtNodeRpcRemote.ts +19 -1
  142. package/src/dht/ExternalApiRpcLocal.ts +5 -5
  143. package/src/dht/ExternalApiRpcRemote.ts +4 -4
  144. package/src/dht/PeerManager.ts +70 -44
  145. package/src/dht/contact/RingContactList.ts +151 -0
  146. package/src/dht/contact/SortedContactList.ts +22 -18
  147. package/src/dht/contact/ringIdentifiers.ts +62 -0
  148. package/src/dht/discovery/DiscoverySession.ts +3 -1
  149. package/src/dht/discovery/PeerDiscovery.ts +45 -6
  150. package/src/dht/discovery/RingDiscoverySession.ts +162 -0
  151. package/src/dht/recursive-operation/RecursiveOperationManager.ts +1 -1
  152. package/src/dht/recursive-operation/RecursiveOperationRpcRemote.ts +1 -1
  153. package/src/dht/recursive-operation/RecursiveOperationSession.ts +1 -3
  154. package/src/dht/routing/Router.ts +0 -2
  155. package/src/dht/routing/RouterRpcLocal.ts +0 -1
  156. package/src/dht/routing/RouterRpcRemote.ts +2 -2
  157. package/src/dht/routing/RoutingSession.ts +2 -2
  158. package/src/dht/store/LocalDataStore.ts +1 -1
  159. package/src/dht/store/StoreManager.ts +2 -2
  160. package/src/exports.ts +3 -2
  161. package/src/helpers/createPeerDescriptor.ts +2 -1
  162. package/src/helpers/version.ts +32 -0
  163. package/src/proto/packages/dht/protos/DhtRpc.client.ts +22 -9
  164. package/src/proto/packages/dht/protos/DhtRpc.server.ts +10 -4
  165. package/src/proto/packages/dht/protos/DhtRpc.ts +122 -100
  166. package/src/transport/RoutingRpcCommunicator.ts +1 -2
  167. package/test/benchmark/Find.test.ts +3 -4
  168. package/test/benchmark/KademliaCorrectness.test.ts +14 -8
  169. package/test/benchmark/RingCorrectness.test.ts +157 -0
  170. package/test/benchmark/WebsocketServerMemoryLeak.test.ts +2 -2
  171. package/test/benchmark/hybrid-network-simulation/RingContactList.test.ts +72 -0
  172. package/test/data/generateGroundTruthData.ts +2 -2
  173. package/test/end-to-end/memory-leak.test.ts +1 -2
  174. package/test/integration/ConnectionManager.test.ts +28 -10
  175. package/test/integration/ConnectivityChecking.test.ts +3 -15
  176. package/test/integration/DhtNodeExternalAPI.test.ts +6 -6
  177. package/test/integration/Find.test.ts +6 -6
  178. package/test/integration/Layer1-scale.test.ts +0 -1
  179. package/test/integration/ReplicateData.test.ts +4 -4
  180. package/test/integration/RouteMessage.test.ts +1 -6
  181. package/test/integration/RouterRpcRemote.test.ts +1 -3
  182. package/test/integration/SimultaneousConnections.test.ts +9 -10
  183. package/test/integration/Store.test.ts +4 -4
  184. package/test/integration/StoreAndDelete.test.ts +5 -5
  185. package/test/integration/StoreOnDhtWithThreeNodes.test.ts +5 -5
  186. package/test/integration/StoreOnDhtWithTwoNodes.test.ts +3 -3
  187. package/test/integration/WebrtcConnectionManagement.test.ts +3 -9
  188. package/test/integration/Websocket.test.ts +2 -2
  189. package/test/integration/WebsocketConnectionManagement.test.ts +1 -6
  190. package/test/integration/rpc-connections-over-webrpc.test.ts +1 -2
  191. package/test/unit/PeerManager.test.ts +44 -10
  192. package/test/unit/RecursiveOperationManager.test.ts +14 -8
  193. package/test/unit/RecursiveOperationSession.test.ts +1 -1
  194. package/test/unit/Router.test.ts +0 -3
  195. package/test/unit/RoutingSession.test.ts +1 -2
  196. package/test/unit/connectivityRequestHandler.test.ts +5 -9
  197. package/test/unit/createPeerDescriptor.test.ts +12 -6
  198. package/test/unit/version.test.ts +18 -0
  199. package/test/utils/utils.ts +60 -47
  200. package/tsconfig.browser.json +2 -1
  201. package/tsconfig.jest.json +4 -2
  202. package/tsconfig.node.json +4 -2
  203. package/dist/src/connection/ConnectionLockHandler.js.map +0 -1
  204. package/dist/src/connection/websocket/ClientWebsocket.d.ts +0 -17
  205. package/dist/src/connection/websocket/ClientWebsocket.js.map +0 -1
  206. package/dist/src/connection/websocket/ServerWebsocket.js.map +0 -1
  207. package/dist/src/helpers/MapWithTtl.d.ts +0 -14
  208. package/dist/src/helpers/MapWithTtl.js +0 -60
  209. package/dist/src/helpers/MapWithTtl.js.map +0 -1
  210. package/dist/src/helpers/versionCompatibility.d.ts +0 -2
  211. package/dist/src/helpers/versionCompatibility.js +0 -18
  212. package/dist/src/helpers/versionCompatibility.js.map +0 -1
  213. package/src/helpers/MapWithTtl.ts +0 -71
  214. package/src/helpers/versionCompatibility.ts +0 -13
  215. package/test/unit/versionCompatibility.test.ts +0 -16
@@ -8,9 +8,12 @@ import {
8
8
  import { DhtNodeRpcRemote } from './DhtNodeRpcRemote'
9
9
  import { RandomContactList } from './contact/RandomContactList'
10
10
  import { SortedContactList } from './contact/SortedContactList'
11
- import { ConnectionManager } from '../connection/ConnectionManager'
11
+ import { ConnectionLocker } from '../connection/ConnectionManager'
12
12
  import EventEmitter from 'eventemitter3'
13
13
  import { DhtAddress, DhtAddressRaw, getNodeIdFromPeerDescriptor, getRawFromDhtAddress } from '../identifiers'
14
+ import { RingContactList, RingContacts } from './contact/RingContactList'
15
+ import { RingIdRaw, getRingIdRawFromPeerDescriptor } from './contact/ringIdentifiers'
16
+ import { LockID } from '../connection/ConnectionLockStates'
14
17
 
15
18
  const logger = new Logger(module)
16
19
 
@@ -19,8 +22,10 @@ interface PeerManagerConfig {
19
22
  maxContactListSize: number
20
23
  peerDiscoveryQueryBatchSize: number
21
24
  localNodeId: DhtAddress
22
- connectionManager: ConnectionManager
25
+ localPeerDescriptor: PeerDescriptor
26
+ connectionLocker?: ConnectionLocker
23
27
  isLayer0: boolean
28
+ lockId: LockID
24
29
  createDhtNodeRpcRemote: (peerDescriptor: PeerDescriptor) => DhtNodeRpcRemote
25
30
  }
26
31
 
@@ -29,6 +34,8 @@ export interface PeerManagerEvents {
29
34
  contactRemoved: (peerDescriptor: PeerDescriptor, closestPeers: PeerDescriptor[]) => void
30
35
  randomContactAdded: (peerDescriptor: PeerDescriptor, closestPeers: PeerDescriptor[]) => void
31
36
  randomContactRemoved: (peerDescriptor: PeerDescriptor, closestPeers: PeerDescriptor[]) => void
37
+ ringContactAdded: (peerDescriptor: PeerDescriptor, closestPeers: RingContacts) => void
38
+ ringContactRemoved: (peerDescriptor: PeerDescriptor, closestPeers: RingContacts) => void
32
39
  kBucketEmpty: () => void
33
40
  }
34
41
 
@@ -51,6 +58,7 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
51
58
  // All nodes that we know about
52
59
  private contacts: SortedContactList<DhtNodeRpcRemote>
53
60
  private randomPeers: RandomContactList<DhtNodeRpcRemote>
61
+ private ringContacts: RingContactList<DhtNodeRpcRemote>
54
62
  private stopped: boolean = false
55
63
  private readonly config: PeerManagerConfig
56
64
 
@@ -62,6 +70,13 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
62
70
  numberOfNodesPerKBucket: this.config.numberOfNodesPerKBucket,
63
71
  numberOfNodesToPing: this.config.numberOfNodesPerKBucket
64
72
  })
73
+ this.ringContacts = new RingContactList<DhtNodeRpcRemote>(getRingIdRawFromPeerDescriptor(this.config.localPeerDescriptor), true)
74
+ this.ringContacts.on('ringContactAdded', (peerDescriptor: PeerDescriptor, closestPeers: RingContacts) => {
75
+ this.emit('ringContactAdded', peerDescriptor, closestPeers)
76
+ })
77
+ this.ringContacts.on('ringContactRemoved', (peerDescriptor: PeerDescriptor, closestPeers: RingContacts) => {
78
+ this.emit('ringContactRemoved', peerDescriptor, closestPeers)
79
+ })
65
80
  this.bucket.on('ping', (oldContacts: DhtNodeRpcRemote[], newContact: DhtNodeRpcRemote) => this.onKBucketPing(oldContacts, newContact))
66
81
  this.bucket.on('removed', (contact: DhtNodeRpcRemote) => this.onKBucketRemoved(getNodeIdFromPeerDescriptor(contact.getPeerDescriptor())))
67
82
  this.bucket.on('added', (contact: DhtNodeRpcRemote) => this.onKBucketAdded(contact))
@@ -69,7 +84,7 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
69
84
  // TODO: Update contact info to the connection manager and reconnect
70
85
  })
71
86
  this.contacts = new SortedContactList({
72
- referenceId: this.config.localNodeId,
87
+ referenceId: this.config.localNodeId,
73
88
  maxSize: this.config.maxContactListSize,
74
89
  allowToContainReferenceId: false,
75
90
  emitEvents: true
@@ -98,7 +113,7 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
98
113
  return
99
114
  }
100
115
  const sortingList: SortedContactList<DhtNodeRpcRemote> = new SortedContactList({
101
- referenceId: this.config.localNodeId,
116
+ referenceId: this.config.localNodeId,
102
117
  maxSize: 100, // TODO use config option or named constant?
103
118
  allowToContainReferenceId: false,
104
119
  emitEvents: false
@@ -106,7 +121,7 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
106
121
  sortingList.addContacts(oldContacts)
107
122
  const sortedContacts = sortingList.getAllContacts()
108
123
  const removableNodeId = sortedContacts[sortedContacts.length - 1].getNodeId()
109
- this.config.connectionManager?.weakUnlockConnection(removableNodeId)
124
+ this.config.connectionLocker?.weakUnlockConnection(removableNodeId, this.config.lockId)
110
125
  this.bucket.remove(getRawFromDhtAddress(removableNodeId))
111
126
  this.bucket.add(newContact)
112
127
  }
@@ -115,7 +130,7 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
115
130
  if (this.stopped) {
116
131
  return
117
132
  }
118
- this.config.connectionManager?.weakUnlockConnection(nodeId)
133
+ this.config.connectionLocker?.weakUnlockConnection(nodeId, this.config.lockId)
119
134
  logger.trace(`Removed contact ${nodeId}`)
120
135
  if (this.bucket.count() === 0) {
121
136
  this.emit('kBucketEmpty')
@@ -130,7 +145,7 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
130
145
  const peerDescriptor = contact.getPeerDescriptor()
131
146
  const nodeId = getNodeIdFromPeerDescriptor(peerDescriptor)
132
147
  // Important to lock here, before the ping result is known
133
- this.config.connectionManager?.weakLockConnection(nodeId)
148
+ this.config.connectionLocker?.weakLockConnection(nodeId, this.config.lockId)
134
149
  if (this.connections.has(contact.getNodeId())) {
135
150
  logger.trace(`Added new contact ${nodeId}`)
136
151
  } else { // open connection by pinging
@@ -140,13 +155,13 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
140
155
  logger.trace(`Added new contact ${nodeId}`)
141
156
  } else {
142
157
  logger.trace('ping failed ' + nodeId)
143
- this.config.connectionManager?.weakUnlockConnection(nodeId)
158
+ this.config.connectionLocker?.weakUnlockConnection(nodeId, this.config.lockId)
144
159
  this.removeContact(nodeId)
145
160
  this.addClosestContactToBucket()
146
161
  }
147
162
  return
148
163
  }).catch((_e) => {
149
- this.config.connectionManager?.weakUnlockConnection(nodeId)
164
+ this.config.connectionLocker?.weakUnlockConnection(nodeId, this.config.lockId)
150
165
  this.removeContact(nodeId)
151
166
  this.addClosestContactToBucket()
152
167
  })
@@ -160,7 +175,7 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
160
175
  }
161
176
  const closest = this.getClosestActiveContactNotInBucket()
162
177
  if (closest) {
163
- this.addContact([closest.getPeerDescriptor()])
178
+ this.addContact(closest.getPeerDescriptor())
164
179
  }
165
180
  }
166
181
 
@@ -207,6 +222,7 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
207
222
  return
208
223
  }
209
224
  logger.trace(`Removing contact ${nodeId}`)
225
+ this.ringContacts.removeContact(this.contacts.getContact(nodeId)?.contact)
210
226
  this.bucket.remove(getRawFromDhtAddress(nodeId))
211
227
  this.contacts.removeContact(nodeId)
212
228
  this.randomPeers.removeContact(nodeId)
@@ -219,6 +235,10 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
219
235
  this.bucket.remove(rpcRemote.id)
220
236
  })
221
237
  this.bucket.removeAllListeners()
238
+ this.ringContacts.getAllContacts().forEach((rpcRemote) => {
239
+ rpcRemote.leaveNotice()
240
+ this.ringContacts.removeContact(rpcRemote)
241
+ })
222
242
  this.contacts.stop()
223
243
  this.randomPeers.stop()
224
244
  this.connections.clear()
@@ -229,10 +249,11 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
229
249
  referenceId,
230
250
  allowToContainReferenceId: true,
231
251
  emitEvents: false,
232
- excludedNodeIds
233
- })
252
+ excludedNodeIds,
253
+ maxSize: limit
254
+ })
234
255
  this.bucket.toArray().forEach((contact) => closest.addContact(contact))
235
- return closest.getClosestContacts(limit)
256
+ return closest.getAllContacts()
236
257
  }
237
258
 
238
259
  // TODO reduce copy-paste?
@@ -241,21 +262,26 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
241
262
  referenceId,
242
263
  allowToContainReferenceId: true,
243
264
  emitEvents: false,
244
- excludedNodeIds
265
+ excludedNodeIds,
266
+ maxSize: limit
245
267
  })
246
268
  this.contacts.getAllContacts().map((contact) => closest.addContact(contact))
247
- // TODO should set the excludeSet and limit to SortedContactList constructor and remove these line
248
- return closest.getClosestContacts(limit)
269
+ return closest.getAllContacts()
270
+ }
271
+
272
+ getClosestRingContactsTo(
273
+ ringIdRaw: RingIdRaw,
274
+ limit?: number,
275
+ excludedIds?: Set<DhtAddress>
276
+ ): { left: DhtNodeRpcRemote[], right: DhtNodeRpcRemote[] } {
277
+ const closest = new RingContactList<DhtNodeRpcRemote>(ringIdRaw, false, excludedIds)
278
+ this.contacts.getAllContacts().map((contact) => closest.addContact(contact))
279
+ this.ringContacts.getAllContacts().map((contact) => closest.addContact(contact))
280
+ return closest.getClosestContacts(limit ?? 8)
249
281
  }
250
282
 
251
283
  getContactCount(excludedNodeIds?: Set<DhtAddress>): number {
252
- return this.contacts.getAllContacts().filter((contact) => {
253
- if (!excludedNodeIds) {
254
- return true
255
- } else {
256
- return !excludedNodeIds.has(contact.getNodeId())
257
- }
258
- }).length
284
+ return this.contacts.getSize(excludedNodeIds)
259
285
  }
260
286
 
261
287
  getConnectionCount(): number {
@@ -274,30 +300,30 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
274
300
  this.contacts.setActive(nodeId)
275
301
  }
276
302
 
277
- addContact(peerDescriptors: PeerDescriptor[], setActive?: boolean): void {
303
+ addContact(peerDescriptor: PeerDescriptor): void {
278
304
  if (this.stopped) {
279
305
  return
280
306
  }
281
- peerDescriptors.forEach((contact) => {
282
- const nodeId = getNodeIdFromPeerDescriptor(contact)
283
- if (nodeId !== this.config.localNodeId) {
284
- logger.trace(`Adding new contact ${nodeId}`)
285
- const remote = this.config.createDhtNodeRpcRemote(contact)
286
- const isInBucket = (this.bucket.get(contact.nodeId) !== null)
287
- const isInContacts = (this.contacts.getContact(nodeId) !== undefined)
288
- if (isInBucket || isInContacts) {
289
- this.randomPeers.addContact(remote)
290
- }
291
- if (!isInBucket) {
292
- this.bucket.add(remote)
293
- }
294
- if (!isInContacts) {
295
- this.contacts.addContact(remote)
296
- }
297
- if (setActive) {
298
- this.contacts.setActive(nodeId)
299
- }
307
+ const nodeId = getNodeIdFromPeerDescriptor(peerDescriptor)
308
+ if (nodeId !== this.config.localNodeId) {
309
+ logger.trace(`Adding new contact ${nodeId}`)
310
+ const remote = this.config.createDhtNodeRpcRemote(peerDescriptor)
311
+ const isInBucket = (this.bucket.get(peerDescriptor.nodeId) !== null)
312
+ const isInContacts = (this.contacts.getContact(nodeId) !== undefined)
313
+ const isInRingContacts = this.ringContacts.getContact(peerDescriptor) !== undefined
314
+
315
+ if (isInBucket || isInContacts) {
316
+ this.randomPeers.addContact(remote)
300
317
  }
301
- })
318
+ if (!isInBucket) {
319
+ this.bucket.add(remote)
320
+ }
321
+ if (!isInContacts) {
322
+ this.contacts.addContact(remote)
323
+ }
324
+ if (!isInRingContacts) {
325
+ this.ringContacts.addContact(remote)
326
+ }
327
+ }
302
328
  }
303
329
  }
@@ -0,0 +1,151 @@
1
+ import { PeerDescriptor } from '../../proto/packages/dht/protos/DhtRpc'
2
+ import { OrderedMap } from '@js-sdsl/ordered-map'
3
+ import { RingDistance, RingId, RingIdRaw, getLeftDistance, getRightDistance, getRingIdFromPeerDescriptor, getRingIdFromRaw } from './ringIdentifiers'
4
+ import { DhtAddress, getNodeIdFromPeerDescriptor } from '../../identifiers'
5
+ import EventEmitter from 'eventemitter3'
6
+
7
+ export interface RingContacts {
8
+ left: PeerDescriptor[]
9
+ right: PeerDescriptor[]
10
+ }
11
+ export interface RingContactListEvents {
12
+ ringContactAdded: (peerDescriptor: PeerDescriptor, closestPeers: RingContacts) => void
13
+ ringContactRemoved: (peerDescriptor: PeerDescriptor, closestPeers: RingContacts) => void
14
+ }
15
+ export class RingContactList<C extends { getPeerDescriptor(): PeerDescriptor }> extends EventEmitter<RingContactListEvents> {
16
+
17
+ private readonly numNeighborsPerSide = 5
18
+ private readonly referenceId: RingId
19
+ private readonly excludedIds: Set<DhtAddress>
20
+ private readonly leftNeighbors: OrderedMap<RingDistance, C>
21
+ private readonly rightNeighbors: OrderedMap<RingDistance, C>
22
+ private readonly emitEvents: boolean
23
+
24
+ constructor(rawReferenceId: RingIdRaw, emitEvents: boolean, excludedIds?: Set<DhtAddress>) {
25
+ super()
26
+ this.referenceId = getRingIdFromRaw(rawReferenceId)
27
+ this.emitEvents = emitEvents
28
+ this.excludedIds = excludedIds ?? new Set()
29
+ this.leftNeighbors = new OrderedMap<RingDistance, C>()
30
+ this.rightNeighbors = new OrderedMap<RingDistance, C>()
31
+ }
32
+
33
+ addContact(contact: C): void {
34
+ const id = getRingIdFromPeerDescriptor(contact.getPeerDescriptor())
35
+ if (id === this.referenceId || this.excludedIds.has(getNodeIdFromPeerDescriptor(contact.getPeerDescriptor()))) {
36
+ return
37
+ }
38
+ let elementAdded = false
39
+ let elementRemoved = false
40
+
41
+ const leftDistance = getLeftDistance(this.referenceId, id)
42
+ const lastLeftNeighbor = this.leftNeighbors.back()
43
+ if (lastLeftNeighbor === undefined || leftDistance < lastLeftNeighbor[0]) {
44
+ this.leftNeighbors.setElement(leftDistance, contact)
45
+ elementAdded = true
46
+ if (this.leftNeighbors.size() > this.numNeighborsPerSide) {
47
+ this.leftNeighbors.eraseElementByIterator(this.leftNeighbors.rBegin())
48
+ elementRemoved = true
49
+ }
50
+ }
51
+
52
+ const rightDistance = getRightDistance(this.referenceId, id)
53
+ const lastRightNeighbor = this.rightNeighbors.back()
54
+ if (lastRightNeighbor === undefined || rightDistance < lastRightNeighbor[0]) {
55
+ this.rightNeighbors.setElement(rightDistance, contact)
56
+ elementAdded = true
57
+ if (this.rightNeighbors.size() > this.numNeighborsPerSide) {
58
+ this.rightNeighbors.eraseElementByIterator(this.rightNeighbors.rBegin())
59
+ elementRemoved = true
60
+ }
61
+ }
62
+
63
+ if (this.emitEvents && (elementAdded || elementRemoved)) {
64
+ const closestContacts = this.getClosestContacts()
65
+ const closestDescriptors = {
66
+ left: closestContacts.left.map((c) => c.getPeerDescriptor()),
67
+ right: closestContacts.right.map((c) => c.getPeerDescriptor())
68
+ }
69
+ if (elementAdded) {
70
+ this.emit('ringContactAdded', contact.getPeerDescriptor(), closestDescriptors)
71
+ }
72
+ if (elementRemoved) {
73
+ this.emit('ringContactRemoved', contact.getPeerDescriptor(), closestDescriptors)
74
+ }
75
+ }
76
+ }
77
+
78
+ removeContact(contact?: C): void {
79
+ if (contact === undefined) {
80
+ return
81
+ }
82
+
83
+ const id = getRingIdFromPeerDescriptor(contact.getPeerDescriptor())
84
+ const leftDistance = getLeftDistance(this.referenceId, id)
85
+ const rightDistance = getRightDistance(this.referenceId, id)
86
+
87
+ let elementRemoved = false
88
+ if (this.leftNeighbors.eraseElementByKey(leftDistance)) {
89
+ elementRemoved = true
90
+ }
91
+ if (this.rightNeighbors.eraseElementByKey(rightDistance)) {
92
+ elementRemoved = true
93
+ }
94
+
95
+ if (this.emitEvents && elementRemoved) {
96
+ const closestContacts = this.getClosestContacts()
97
+ const closestDescriptors = { left: closestContacts.left.map((c) => c.getPeerDescriptor()),
98
+ right: closestContacts.right.map((c) => c.getPeerDescriptor()) }
99
+ this.emit('ringContactRemoved', contact.getPeerDescriptor(), closestDescriptors)
100
+ }
101
+ }
102
+
103
+ getContact(peerDescriptor: PeerDescriptor): C | undefined {
104
+ const id = getRingIdFromPeerDescriptor(peerDescriptor)
105
+ const leftDistance = getLeftDistance(this.referenceId, id)
106
+ const rightDistance = getRightDistance(this.referenceId, id)
107
+ if (this.leftNeighbors.getElementByKey(leftDistance)) {
108
+ return this.leftNeighbors.getElementByKey(leftDistance)
109
+ }
110
+ if (this.rightNeighbors.getElementByKey(rightDistance)) {
111
+ return this.rightNeighbors.getElementByKey(rightDistance)
112
+ }
113
+ return undefined
114
+ }
115
+
116
+ getClosestContacts(limitPerSide?: number): { left: C[], right: C[] } {
117
+ const leftContacts: C[] = []
118
+ const rightContacts: C[] = []
119
+
120
+ let leftCount = 0
121
+ for (const item of this.leftNeighbors) {
122
+ if (limitPerSide != undefined && leftCount >= limitPerSide) {
123
+ break
124
+ }
125
+ leftContacts.push(item[1])
126
+ leftCount++
127
+ }
128
+
129
+ let rightCount = 0
130
+ for (const item of this.rightNeighbors) {
131
+ if (limitPerSide != undefined && rightCount >= limitPerSide) {
132
+ break
133
+ }
134
+ rightContacts.push(item[1])
135
+ rightCount++
136
+ }
137
+
138
+ return { left: leftContacts, right: rightContacts }
139
+ }
140
+
141
+ getAllContacts(): C[] {
142
+ const ret: C[] = []
143
+ for (const item of this.leftNeighbors) {
144
+ ret.push(item[1])
145
+ }
146
+ for (const item of this.rightNeighbors) {
147
+ ret.push(item[1])
148
+ }
149
+ return ret
150
+ }
151
+ }
@@ -92,6 +92,10 @@ export class SortedContactList<C extends { getNodeId: () => DhtAddress }> extend
92
92
  return this.contactsById.get(id)
93
93
  }
94
94
 
95
+ has(id: DhtAddress): boolean {
96
+ return this.contactsById.has(id)
97
+ }
98
+
95
99
  public setContacted(contactId: DhtAddress): void {
96
100
  if (this.contactsById.has(contactId)) {
97
101
  this.contactsById.get(contactId)!.contacted = true
@@ -105,25 +109,17 @@ export class SortedContactList<C extends { getNodeId: () => DhtAddress }> extend
105
109
  }
106
110
 
107
111
  public getClosestContacts(limit?: number): C[] {
108
- const ret: C[] = []
109
- this.contactIds.forEach((contactId) => {
110
- const contact = this.contactsById.get(contactId)
111
- if (contact) {
112
- ret.push(contact.contact)
113
- }
114
- })
115
- if (limit === undefined) {
116
- return ret
117
- } else {
118
- return ret.slice(0, limit)
119
- }
112
+ const ret = this.getAllContacts()
113
+ return (limit === undefined)
114
+ ? ret
115
+ : ret.slice(0, limit)
120
116
  }
121
117
 
122
118
  public getUncontactedContacts(num: number): C[] {
123
119
  const ret: C[] = []
124
120
  for (const contactId of this.contactIds) {
125
- const contact = this.contactsById.get(contactId)
126
- if (contact && !contact.contacted) {
121
+ const contact = this.contactsById.get(contactId)!
122
+ if (!contact.contacted) {
127
123
  ret.push(contact.contact)
128
124
  if (ret.length >= num) {
129
125
  return ret
@@ -136,8 +132,8 @@ export class SortedContactList<C extends { getNodeId: () => DhtAddress }> extend
136
132
  public getActiveContacts(limit?: number): C[] {
137
133
  const ret: C[] = []
138
134
  this.contactIds.forEach((contactId) => {
139
- const contact = this.contactsById.get(contactId)
140
- if (contact && contact.active) {
135
+ const contact = this.contactsById.get(contactId)!
136
+ if (contact.active) {
141
137
  ret.push(contact.contact)
142
138
  }
143
139
  })
@@ -187,8 +183,16 @@ export class SortedContactList<C extends { getNodeId: () => DhtAddress }> extend
187
183
  return this.contactIds.map((nodeId) => this.contactsById.get(nodeId)!.contact)
188
184
  }
189
185
 
190
- public getSize(): number {
191
- return this.contactIds.length
186
+ public getSize(excludedNodeIds?: Set<DhtAddress>): number {
187
+ let excludedCount = 0
188
+ if (excludedNodeIds !== undefined) {
189
+ for (const nodeId of excludedNodeIds) {
190
+ if (this.has(nodeId)) {
191
+ excludedCount++
192
+ }
193
+ }
194
+ }
195
+ return this.contactIds.length - excludedCount
192
196
  }
193
197
 
194
198
  public clear(): void {
@@ -0,0 +1,62 @@
1
+ import { PeerDescriptor } from '../../proto/packages/dht/protos/DhtRpc'
2
+
3
+ // Notice: you cannot convert RingId to RingIdRaw, because
4
+ // RingId is only an approximation of the actual value.
5
+ // That is why RingIdRaw is widely used in the codebase.
6
+
7
+ export type RingIdRaw = Uint8Array & { __ringIdRaw: never }
8
+ export type RingId = number & { __ringId: never }
9
+ export type RingDistance = number & { __ringDistance: never }
10
+
11
+ export const RING_SIZE = 2 ** 120 - 1 // 2^120 - 1
12
+
13
+ const binaryToBigInt = (binary: Uint8Array): bigint => {
14
+ return binary.reduce((acc, val) => (acc << BigInt(8)) | BigInt(val), BigInt(0))
15
+ }
16
+
17
+ export const getRingIdFromRaw = (raw: RingIdRaw): RingId => Number(binaryToBigInt(raw)) as RingId
18
+
19
+ export const getRingIdRawFromPeerDescriptor = (peerDescriptor: PeerDescriptor): RingIdRaw => {
20
+ const regionAsBuffer = Buffer.alloc(4)
21
+ regionAsBuffer.writeUInt32BE(peerDescriptor.region ?? 0, 0)
22
+ const ipAsbuffer = Buffer.alloc(4)
23
+ ipAsbuffer.writeUInt32BE(peerDescriptor.ipAddress ?? 0, 0)
24
+
25
+ const uniquePartAsBuffer = Buffer.from(peerDescriptor.nodeId.subarray(peerDescriptor.nodeId.length - 7, peerDescriptor.nodeId.length))
26
+
27
+ const arr = [
28
+ regionAsBuffer,
29
+ ipAsbuffer,
30
+ uniquePartAsBuffer
31
+ ]
32
+
33
+ const buffer = Buffer.concat(arr)
34
+ return new Uint8Array(buffer) as RingIdRaw
35
+ }
36
+
37
+ export const getRingIdFromPeerDescriptor = (peerDescriptor: PeerDescriptor): RingId => {
38
+ const raw = getRingIdRawFromPeerDescriptor(peerDescriptor)
39
+ return Number(binaryToBigInt(raw)) as RingId
40
+ }
41
+
42
+ export const getLeftDistance = (referenceId: RingId, id: RingId): RingDistance =>{
43
+ const diff = Math.abs(referenceId - id)
44
+ if (referenceId > id) {
45
+ // if id is smaller than referenceId, then the distance is the difference
46
+ return diff as RingDistance
47
+ } else {
48
+ // if id is bigger than referenceId, then the distance is the ringSize - difference
49
+ return RING_SIZE - diff as RingDistance
50
+ }
51
+ }
52
+
53
+ export const getRightDistance = (referenceId: RingId, id: RingId): RingDistance => {
54
+ const diff = Math.abs(referenceId - id)
55
+ if (referenceId > id) {
56
+ // if id is smaller than referenceId, then the distance is the ringSize - difference
57
+ return RING_SIZE - diff as RingDistance
58
+ } else {
59
+ // if id is bigger than referenceId, then the distance is the difference
60
+ return diff as RingDistance
61
+ }
62
+ }
@@ -38,7 +38,9 @@ export class DiscoverySession {
38
38
  if (this.stopped) {
39
39
  return
40
40
  }
41
- this.config.peerManager.addContact(contacts)
41
+ for (const contact of contacts) {
42
+ this.config.peerManager.addContact(contact)
43
+ }
42
44
  }
43
45
 
44
46
  private async getClosestPeersFromContact(contact: DhtNodeRpcRemote): Promise<PeerDescriptor[]> {
@@ -2,10 +2,12 @@ import { DiscoverySession } from './DiscoverySession'
2
2
  import { DhtNodeRpcRemote } from '../DhtNodeRpcRemote'
3
3
  import { PeerDescriptor } from '../../proto/packages/dht/protos/DhtRpc'
4
4
  import { Logger, scheduleAtInterval, setAbortableTimeout } from '@streamr/utils'
5
- import { ConnectionManager } from '../../connection/ConnectionManager'
5
+ import { ConnectionLocker } from '../../connection/ConnectionManager'
6
6
  import { PeerManager } from '../PeerManager'
7
7
  import { DhtAddress, areEqualPeerDescriptors, getDhtAddressFromRaw, getNodeIdFromPeerDescriptor, getRawFromDhtAddress } from '../../identifiers'
8
8
  import { ServiceID } from '../../types/ServiceID'
9
+ import { RingDiscoverySession } from './RingDiscoverySession'
10
+ import { RingIdRaw, getRingIdRawFromPeerDescriptor } from '../contact/ringIdentifiers'
9
11
 
10
12
  interface PeerDiscoveryConfig {
11
13
  localPeerDescriptor: PeerDescriptor
@@ -14,7 +16,7 @@ interface PeerDiscoveryConfig {
14
16
  serviceId: ServiceID
15
17
  parallelism: number
16
18
  joinTimeout: number
17
- connectionManager?: ConnectionManager
19
+ connectionLocker?: ConnectionLocker
18
20
  peerManager: PeerManager
19
21
  }
20
22
 
@@ -29,6 +31,8 @@ const logger = new Logger(module)
29
31
  export class PeerDiscovery {
30
32
 
31
33
  private ongoingDiscoverySessions: Map<string, DiscoverySession> = new Map()
34
+ private ongoingRingDiscoverySessions: Map<string, RingDiscoverySession> = new Map()
35
+
32
36
  private rejoinOngoing = false
33
37
  private joinCalled = false
34
38
  private readonly abortController: AbortController
@@ -74,18 +78,24 @@ export class PeerDiscovery {
74
78
  if (areEqualPeerDescriptors(entryPointDescriptor, this.config.localPeerDescriptor)) {
75
79
  return
76
80
  }
77
- this.config.connectionManager?.lockConnection(entryPointDescriptor, `${this.config.serviceId}::joinDht`)
78
- this.config.peerManager.addContact([entryPointDescriptor])
81
+ this.config.connectionLocker?.lockConnection(entryPointDescriptor, `${this.config.serviceId}::joinDht`)
82
+ this.config.peerManager.addContact(entryPointDescriptor)
79
83
  const targetId = getNodeIdFromPeerDescriptor(this.config.localPeerDescriptor)
80
84
  const sessions = [this.createSession(targetId, contactedPeers)]
81
85
  if (additionalDistantJoin.enabled) {
82
86
  sessions.push(this.createSession(createDistantDhtAddress(targetId), additionalDistantJoin.contactedPeers))
83
87
  }
84
88
  await this.runSessions(sessions, entryPointDescriptor, retry)
85
- this.config.connectionManager?.unlockConnection(entryPointDescriptor, `${this.config.serviceId}::joinDht`)
89
+ this.config.connectionLocker?.unlockConnection(entryPointDescriptor, `${this.config.serviceId}::joinDht`)
86
90
 
87
91
  }
88
92
 
93
+ async joinRing(): Promise<void> {
94
+ const contactedPeers = new Set<DhtAddress>()
95
+ const sessions = [this.createRingSession(getRingIdRawFromPeerDescriptor(this.config.localPeerDescriptor), contactedPeers)]
96
+ await this.runRingSessions(sessions)
97
+ }
98
+
89
99
  private createSession(targetId: DhtAddress, contactedPeers: Set<DhtAddress>): DiscoverySession {
90
100
  const sessionOptions = {
91
101
  targetId,
@@ -97,6 +107,17 @@ export class PeerDiscovery {
97
107
  return new DiscoverySession(sessionOptions)
98
108
  }
99
109
 
110
+ private createRingSession(targetId: RingIdRaw, contactedPeers: Set<DhtAddress>): RingDiscoverySession {
111
+ const sessionOptions = {
112
+ targetId,
113
+ parallelism: this.config.parallelism,
114
+ noProgressLimit: this.config.joinNoProgressLimit,
115
+ peerManager: this.config.peerManager,
116
+ contactedPeers
117
+ }
118
+ return new RingDiscoverySession(sessionOptions)
119
+ }
120
+
100
121
  private async runSessions(sessions: DiscoverySession[], entryPointDescriptor: PeerDescriptor, retry: boolean): Promise<void> {
101
122
  try {
102
123
  for (const session of sessions) {
@@ -121,6 +142,19 @@ export class PeerDiscovery {
121
142
  }
122
143
  }
123
144
 
145
+ private async runRingSessions(sessions: RingDiscoverySession[]): Promise<void> {
146
+ try {
147
+ for (const session of sessions) {
148
+ this.ongoingRingDiscoverySessions.set(session.id, session)
149
+ await session.findClosestNodes(this.config.joinTimeout)
150
+ }
151
+ } catch (_e) {
152
+ logger.debug(`Ring join on ${this.config.serviceId} timed out`)
153
+ } finally {
154
+ sessions.forEach((session) => this.ongoingDiscoverySessions.delete(session.id))
155
+ }
156
+ }
157
+
124
158
  public async rejoinDht(entryPoint: PeerDescriptor): Promise<void> {
125
159
  if (this.isStopped() || this.rejoinOngoing) {
126
160
  return
@@ -162,7 +196,9 @@ export class PeerDiscovery {
162
196
  await Promise.allSettled(
163
197
  nodes.map(async (peer: DhtNodeRpcRemote) => {
164
198
  const contacts = await peer.getClosestPeers(localNodeId)
165
- this.config.peerManager.addContact(contacts)
199
+ for (const contact of contacts) {
200
+ this.config.peerManager.addContact(contact)
201
+ }
166
202
  })
167
203
  )
168
204
  }
@@ -184,5 +220,8 @@ export class PeerDiscovery {
184
220
  this.ongoingDiscoverySessions.forEach((session) => {
185
221
  session.stop()
186
222
  })
223
+ this.ongoingRingDiscoverySessions.forEach((session) => {
224
+ session.stop()
225
+ })
187
226
  }
188
227
  }