@streamr/dht 0.0.1-tatum.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.
- package/.eslintignore +5 -0
- package/.eslintrc +3 -0
- package/README.md +38 -0
- package/dist/src/connection/Connection.d.ts +11 -0
- package/dist/src/connection/Connection.js +23 -0
- package/dist/src/connection/Connection.js.map +1 -0
- package/dist/src/connection/ConnectionLockHandler.d.ts +23 -0
- package/dist/src/connection/ConnectionLockHandler.js +94 -0
- package/dist/src/connection/ConnectionLockHandler.js.map +1 -0
- package/dist/src/connection/ConnectionManager.d.ts +94 -0
- package/dist/src/connection/ConnectionManager.js +554 -0
- package/dist/src/connection/ConnectionManager.js.map +1 -0
- package/dist/src/connection/ConnectivityChecker.d.ts +17 -0
- package/dist/src/connection/ConnectivityChecker.js +187 -0
- package/dist/src/connection/ConnectivityChecker.js.map +1 -0
- package/dist/src/connection/Handshaker.d.ts +19 -0
- package/dist/src/connection/Handshaker.js +77 -0
- package/dist/src/connection/Handshaker.js.map +1 -0
- package/dist/src/connection/IConnection.d.ts +38 -0
- package/dist/src/connection/IConnection.js +19 -0
- package/dist/src/connection/IConnection.js.map +1 -0
- package/dist/src/connection/IConnectionSource.d.ts +4 -0
- package/dist/src/connection/IConnectionSource.js +3 -0
- package/dist/src/connection/IConnectionSource.js.map +1 -0
- package/dist/src/connection/ManagedConnection.d.ts +60 -0
- package/dist/src/connection/ManagedConnection.js +352 -0
- package/dist/src/connection/ManagedConnection.js.map +1 -0
- package/dist/src/connection/ManagedWebRtcConnection.d.ts +7 -0
- package/dist/src/connection/ManagedWebRtcConnection.js +20 -0
- package/dist/src/connection/ManagedWebRtcConnection.js.map +1 -0
- package/dist/src/connection/RemoteConnectionLocker.d.ts +14 -0
- package/dist/src/connection/RemoteConnectionLocker.js +93 -0
- package/dist/src/connection/RemoteConnectionLocker.js.map +1 -0
- package/dist/src/connection/Simulator/Simulator.d.ts +42 -0
- package/dist/src/connection/Simulator/Simulator.js +325 -0
- package/dist/src/connection/Simulator/Simulator.js.map +1 -0
- package/dist/src/connection/Simulator/SimulatorConnection.d.ts +19 -0
- package/dist/src/connection/Simulator/SimulatorConnection.js +118 -0
- package/dist/src/connection/Simulator/SimulatorConnection.js.map +1 -0
- package/dist/src/connection/Simulator/SimulatorConnector.d.ts +17 -0
- package/dist/src/connection/Simulator/SimulatorConnector.js +72 -0
- package/dist/src/connection/Simulator/SimulatorConnector.js.map +1 -0
- package/dist/src/connection/Simulator/SimulatorTransport.d.ts +6 -0
- package/dist/src/connection/Simulator/SimulatorTransport.js +11 -0
- package/dist/src/connection/Simulator/SimulatorTransport.js.map +1 -0
- package/dist/src/connection/Simulator/pings.d.ts +21 -0
- package/dist/src/connection/Simulator/pings.js +61 -0
- package/dist/src/connection/Simulator/pings.js.map +1 -0
- package/dist/src/connection/WebRTC/IWebRtcConnection.d.ts +20 -0
- package/dist/src/connection/WebRTC/IWebRtcConnection.js +9 -0
- package/dist/src/connection/WebRTC/IWebRtcConnection.js.map +1 -0
- package/dist/src/connection/WebRTC/NodeWebRtcConnection.d.ts +47 -0
- package/dist/src/connection/WebRTC/NodeWebRtcConnection.js +233 -0
- package/dist/src/connection/WebRTC/NodeWebRtcConnection.js.map +1 -0
- package/dist/src/connection/WebRTC/RemoteWebrtcConnector.d.ts +12 -0
- package/dist/src/connection/WebRTC/RemoteWebrtcConnector.js +74 -0
- package/dist/src/connection/WebRTC/RemoteWebrtcConnector.js.map +1 -0
- package/dist/src/connection/WebRTC/WebRtcConnector.d.ts +47 -0
- package/dist/src/connection/WebRTC/WebRtcConnector.js +227 -0
- package/dist/src/connection/WebRTC/WebRtcConnector.js.map +1 -0
- package/dist/src/connection/WebRTC/iceServerAsString.d.ts +2 -0
- package/dist/src/connection/WebRTC/iceServerAsString.js +18 -0
- package/dist/src/connection/WebRTC/iceServerAsString.js.map +1 -0
- package/dist/src/connection/WebSocket/ClientWebSocket.d.ts +15 -0
- package/dist/src/connection/WebSocket/ClientWebSocket.js +113 -0
- package/dist/src/connection/WebSocket/ClientWebSocket.js.map +1 -0
- package/dist/src/connection/WebSocket/RemoteWebSocketConnector.d.ts +9 -0
- package/dist/src/connection/WebSocket/RemoteWebSocketConnector.js +63 -0
- package/dist/src/connection/WebSocket/RemoteWebSocketConnector.js.map +1 -0
- package/dist/src/connection/WebSocket/ServerWebSocket.d.ts +18 -0
- package/dist/src/connection/WebSocket/ServerWebSocket.js +103 -0
- package/dist/src/connection/WebSocket/ServerWebSocket.js.map +1 -0
- package/dist/src/connection/WebSocket/WebSocketConnector.d.ts +31 -0
- package/dist/src/connection/WebSocket/WebSocketConnector.js +202 -0
- package/dist/src/connection/WebSocket/WebSocketConnector.js.map +1 -0
- package/dist/src/connection/WebSocket/WebSocketServer.d.ts +9 -0
- package/dist/src/connection/WebSocket/WebSocketServer.js +101 -0
- package/dist/src/connection/WebSocket/WebSocketServer.js.map +1 -0
- package/dist/src/dht/DhtNode.d.ts +153 -0
- package/dist/src/dht/DhtNode.js +599 -0
- package/dist/src/dht/DhtNode.js.map +1 -0
- package/dist/src/dht/DhtPeer.d.ts +18 -0
- package/dist/src/dht/DhtPeer.js +74 -0
- package/dist/src/dht/DhtPeer.js.map +1 -0
- package/dist/src/dht/ExternalApi.d.ts +8 -0
- package/dist/src/dht/ExternalApi.js +26 -0
- package/dist/src/dht/ExternalApi.js.map +1 -0
- package/dist/src/dht/RemoteExternalApi.d.ts +6 -0
- package/dist/src/dht/RemoteExternalApi.js +26 -0
- package/dist/src/dht/RemoteExternalApi.js.map +1 -0
- package/dist/src/dht/contact/Contact.d.ts +22 -0
- package/dist/src/dht/contact/Contact.js +25 -0
- package/dist/src/dht/contact/Contact.js.map +1 -0
- package/dist/src/dht/contact/RandomContactList.d.ts +20 -0
- package/dist/src/dht/contact/RandomContactList.js +78 -0
- package/dist/src/dht/contact/RandomContactList.js.map +1 -0
- package/dist/src/dht/contact/Remote.d.ts +15 -0
- package/dist/src/dht/contact/Remote.js +24 -0
- package/dist/src/dht/contact/Remote.js.map +1 -0
- package/dist/src/dht/contact/SortedContactList.d.ts +35 -0
- package/dist/src/dht/contact/SortedContactList.js +156 -0
- package/dist/src/dht/contact/SortedContactList.js.map +1 -0
- package/dist/src/dht/discovery/DiscoverySession.d.ts +36 -0
- package/dist/src/dht/discovery/DiscoverySession.js +116 -0
- package/dist/src/dht/discovery/DiscoverySession.js.map +1 -0
- package/dist/src/dht/discovery/PeerDiscovery.d.ts +42 -0
- package/dist/src/dht/discovery/PeerDiscovery.js +157 -0
- package/dist/src/dht/discovery/PeerDiscovery.js.map +1 -0
- package/dist/src/dht/find/RecursiveFindSession.d.ts +46 -0
- package/dist/src/dht/find/RecursiveFindSession.js +142 -0
- package/dist/src/dht/find/RecursiveFindSession.js.map +1 -0
- package/dist/src/dht/find/RecursiveFinder.d.ts +54 -0
- package/dist/src/dht/find/RecursiveFinder.js +180 -0
- package/dist/src/dht/find/RecursiveFinder.js.map +1 -0
- package/dist/src/dht/find/RemoteRecursiveFindSession.d.ts +6 -0
- package/dist/src/dht/find/RemoteRecursiveFindSession.js +25 -0
- package/dist/src/dht/find/RemoteRecursiveFindSession.js.map +1 -0
- package/dist/src/dht/routing/DuplicateDetector.d.ts +13 -0
- package/dist/src/dht/routing/DuplicateDetector.js +41 -0
- package/dist/src/dht/routing/DuplicateDetector.js.map +1 -0
- package/dist/src/dht/routing/RemoteRouter.d.ts +8 -0
- package/dist/src/dht/routing/RemoteRouter.js +106 -0
- package/dist/src/dht/routing/RemoteRouter.js.map +1 -0
- package/dist/src/dht/routing/Router.d.ts +60 -0
- package/dist/src/dht/routing/Router.js +207 -0
- package/dist/src/dht/routing/Router.js.map +1 -0
- package/dist/src/dht/routing/RoutingSession.d.ts +42 -0
- package/dist/src/dht/routing/RoutingSession.js +178 -0
- package/dist/src/dht/routing/RoutingSession.js.map +1 -0
- package/dist/src/dht/store/DataStore.d.ts +45 -0
- package/dist/src/dht/store/DataStore.js +244 -0
- package/dist/src/dht/store/DataStore.js.map +1 -0
- package/dist/src/dht/store/LocalDataStore.d.ts +19 -0
- package/dist/src/dht/store/LocalDataStore.js +104 -0
- package/dist/src/dht/store/LocalDataStore.js.map +1 -0
- package/dist/src/dht/store/RemoteStore.d.ts +8 -0
- package/dist/src/dht/store/RemoteStore.js +44 -0
- package/dist/src/dht/store/RemoteStore.js.map +1 -0
- package/dist/src/exports.d.ts +19 -0
- package/dist/src/exports.js +41 -0
- package/dist/src/exports.js.map +1 -0
- package/dist/src/helpers/AddressTools.d.ts +2 -0
- package/dist/src/helpers/AddressTools.js +31 -0
- package/dist/src/helpers/AddressTools.js.map +1 -0
- package/dist/src/helpers/PeerID.d.ts +25 -0
- package/dist/src/helpers/PeerID.js +84 -0
- package/dist/src/helpers/PeerID.js.map +1 -0
- package/dist/src/helpers/UUID.d.ts +7 -0
- package/dist/src/helpers/UUID.js +32 -0
- package/dist/src/helpers/UUID.js.map +1 -0
- package/dist/src/helpers/debugHelpers.d.ts +3 -0
- package/dist/src/helpers/debugHelpers.js +11 -0
- package/dist/src/helpers/debugHelpers.js.map +1 -0
- package/dist/src/helpers/errors.d.ts +72 -0
- package/dist/src/helpers/errors.js +95 -0
- package/dist/src/helpers/errors.js.map +1 -0
- package/dist/src/helpers/peerIdFromPeerDescriptor.d.ts +5 -0
- package/dist/src/helpers/peerIdFromPeerDescriptor.js +17 -0
- package/dist/src/helpers/peerIdFromPeerDescriptor.js.map +1 -0
- package/dist/src/helpers/protoClasses.d.ts +2 -0
- package/dist/src/helpers/protoClasses.js +35 -0
- package/dist/src/helpers/protoClasses.js.map +1 -0
- package/dist/src/helpers/protoToString.d.ts +2 -0
- package/dist/src/helpers/protoToString.js +20 -0
- package/dist/src/helpers/protoToString.js.map +1 -0
- package/dist/src/proto/google/protobuf/any.d.ts +173 -0
- package/dist/src/proto/google/protobuf/any.js +155 -0
- package/dist/src/proto/google/protobuf/any.js.map +1 -0
- package/dist/src/proto/google/protobuf/empty.d.ts +32 -0
- package/dist/src/proto/google/protobuf/empty.js +34 -0
- package/dist/src/proto/google/protobuf/empty.js.map +1 -0
- package/dist/src/proto/google/protobuf/timestamp.d.ts +149 -0
- package/dist/src/proto/google/protobuf/timestamp.js +136 -0
- package/dist/src/proto/google/protobuf/timestamp.js.map +1 -0
- package/dist/src/proto/packages/dht/protos/DhtRpc.client.d.ts +320 -0
- package/dist/src/proto/packages/dht/protos/DhtRpc.client.js +245 -0
- package/dist/src/proto/packages/dht/protos/DhtRpc.client.js.map +1 -0
- package/dist/src/proto/packages/dht/protos/DhtRpc.d.ts +1089 -0
- package/dist/src/proto/packages/dht/protos/DhtRpc.js +710 -0
- package/dist/src/proto/packages/dht/protos/DhtRpc.js.map +1 -0
- package/dist/src/proto/packages/dht/protos/DhtRpc.server.d.ts +145 -0
- package/dist/src/proto/packages/dht/protos/DhtRpc.server.js +3 -0
- package/dist/src/proto/packages/dht/protos/DhtRpc.server.js.map +1 -0
- package/dist/src/proto/packages/proto-rpc/protos/ProtoRpc.d.ts +87 -0
- package/dist/src/proto/packages/proto-rpc/protos/ProtoRpc.js +66 -0
- package/dist/src/proto/packages/proto-rpc/protos/ProtoRpc.js.map +1 -0
- package/dist/src/proto/tests.d.ts +39 -0
- package/dist/src/proto/tests.js +34 -0
- package/dist/src/proto/tests.js.map +1 -0
- package/dist/src/rpc-protocol/DhtCallContext.d.ts +12 -0
- package/dist/src/rpc-protocol/DhtCallContext.js +8 -0
- package/dist/src/rpc-protocol/DhtCallContext.js.map +1 -0
- package/dist/src/rpc-protocol/DhtRpcOptions.d.ts +8 -0
- package/dist/src/rpc-protocol/DhtRpcOptions.js +3 -0
- package/dist/src/rpc-protocol/DhtRpcOptions.js.map +1 -0
- package/dist/src/transport/ITransport.d.ts +22 -0
- package/dist/src/transport/ITransport.js +3 -0
- package/dist/src/transport/ITransport.js.map +1 -0
- package/dist/src/transport/ListeningRpcCommunicator.d.ts +6 -0
- package/dist/src/transport/ListeningRpcCommunicator.js +14 -0
- package/dist/src/transport/ListeningRpcCommunicator.js.map +1 -0
- package/dist/src/transport/RoutingRpcCommunicator.d.ts +8 -0
- package/dist/src/transport/RoutingRpcCommunicator.js +52 -0
- package/dist/src/transport/RoutingRpcCommunicator.js.map +1 -0
- package/jest.config.js +2 -0
- package/karma.config.js +20 -0
- package/package.json +64 -0
- package/proto.sh +3 -0
- package/protos/DhtRpc.proto +330 -0
- package/protos/tests.proto +16 -0
- package/src/connection/Connection.ts +23 -0
- package/src/connection/ConnectionLockHandler.ts +105 -0
- package/src/connection/ConnectionManager.ts +676 -0
- package/src/connection/ConnectivityChecker.ts +173 -0
- package/src/connection/Handshaker.ts +92 -0
- package/src/connection/IConnection.ts +47 -0
- package/src/connection/IConnectionSource.ts +6 -0
- package/src/connection/ManagedConnection.ts +398 -0
- package/src/connection/ManagedWebRtcConnection.ts +27 -0
- package/src/connection/RemoteConnectionLocker.ts +88 -0
- package/src/connection/Simulator/Simulator.ts +399 -0
- package/src/connection/Simulator/SimulatorConnection.ts +137 -0
- package/src/connection/Simulator/SimulatorConnector.ts +104 -0
- package/src/connection/Simulator/SimulatorTransport.ts +9 -0
- package/src/connection/Simulator/pings.ts +42 -0
- package/src/connection/WebRTC/BrowserWebRtcConnection.ts +227 -0
- package/src/connection/WebRTC/IWebRtcConnection.ts +24 -0
- package/src/connection/WebRTC/NodeWebRtcConnection.ts +256 -0
- package/src/connection/WebRTC/RemoteWebrtcConnector.ts +93 -0
- package/src/connection/WebRTC/WebRtcConnector.ts +306 -0
- package/src/connection/WebRTC/iceServerAsString.ts +15 -0
- package/src/connection/WebSocket/ClientWebSocket.ts +118 -0
- package/src/connection/WebSocket/RemoteWebSocketConnector.ts +49 -0
- package/src/connection/WebSocket/ServerWebSocket.ts +119 -0
- package/src/connection/WebSocket/WebSocketConnector.ts +264 -0
- package/src/connection/WebSocket/WebSocketServer.ts +97 -0
- package/src/dht/DhtNode.ts +776 -0
- package/src/dht/DhtPeer.ts +96 -0
- package/src/dht/ExternalApi.ts +29 -0
- package/src/dht/RemoteExternalApi.ts +25 -0
- package/src/dht/contact/Contact.ts +36 -0
- package/src/dht/contact/RandomContactList.ts +92 -0
- package/src/dht/contact/Remote.ts +40 -0
- package/src/dht/contact/SortedContactList.ts +196 -0
- package/src/dht/discovery/DiscoverySession.ts +150 -0
- package/src/dht/discovery/PeerDiscovery.ts +162 -0
- package/src/dht/find/RecursiveFindSession.ts +178 -0
- package/src/dht/find/RecursiveFinder.ts +272 -0
- package/src/dht/find/RemoteRecursiveFindSession.ts +33 -0
- package/src/dht/routing/DuplicateDetector.ts +53 -0
- package/src/dht/routing/RemoteRouter.ts +115 -0
- package/src/dht/routing/Router.ts +266 -0
- package/src/dht/routing/RoutingSession.ts +222 -0
- package/src/dht/store/DataStore.ts +321 -0
- package/src/dht/store/LocalDataStore.ts +114 -0
- package/src/dht/store/RemoteStore.ts +58 -0
- package/src/exports.ts +19 -0
- package/src/helpers/AddressTools.ts +26 -0
- package/src/helpers/PeerID.ts +95 -0
- package/src/helpers/UUID.ts +30 -0
- package/src/helpers/debugHelpers.ts +9 -0
- package/src/helpers/errors.ts +49 -0
- package/src/helpers/peerIdFromPeerDescriptor.ts +14 -0
- package/src/helpers/protoClasses.ts +63 -0
- package/src/helpers/protoToString.ts +21 -0
- package/src/proto/google/protobuf/any.ts +319 -0
- package/src/proto/google/protobuf/empty.ts +84 -0
- package/src/proto/google/protobuf/timestamp.ts +281 -0
- package/src/proto/packages/dht/protos/DhtRpc.client.ts +373 -0
- package/src/proto/packages/dht/protos/DhtRpc.server.ts +148 -0
- package/src/proto/packages/dht/protos/DhtRpc.ts +1399 -0
- package/src/proto/packages/proto-rpc/protos/ProtoRpc.ts +108 -0
- package/src/proto/tests.ts +52 -0
- package/src/rpc-protocol/DhtCallContext.ts +15 -0
- package/src/rpc-protocol/DhtRpcOptions.ts +9 -0
- package/src/transport/ITransport.ts +31 -0
- package/src/transport/ListeningRpcCommunicator.ts +14 -0
- package/src/transport/RoutingRpcCommunicator.ts +59 -0
- package/src/types/glogal.d.ts +1 -0
- package/src/types/textencoding.d.ts +7 -0
- package/test/RandomGraphSimulation.ts +52 -0
- package/test/benchmark/KademliaCorrectness.test.ts +115 -0
- package/test/benchmark/RecursiveFind.test.ts +87 -0
- package/test/benchmark/any.test.ts +28 -0
- package/test/benchmark/kademlia-simulation/Contact.ts +32 -0
- package/test/benchmark/kademlia-simulation/KademliaSimulation.ts +94 -0
- package/test/benchmark/kademlia-simulation/SimulationNode.ts +125 -0
- package/test/data/generateGroundTruthData.ts +70 -0
- package/test/end-to-end/Layer0-Layer1.test.ts +87 -0
- package/test/end-to-end/Layer0.test.ts +60 -0
- package/test/end-to-end/Layer0MixedConnectionTypes.test.ts +94 -0
- package/test/end-to-end/Layer0WebRTC-Layer1.test.ts +134 -0
- package/test/end-to-end/Layer0WebRTC.test.ts +98 -0
- package/test/end-to-end/Layer1-Scale-WebRTC.test.ts +69 -0
- package/test/end-to-end/Layer1-Scale-WebSocket.test.ts +73 -0
- package/test/end-to-end/WebSocketConnectionRequest.test.ts +62 -0
- package/test/integration/ConnectionLocking.test.ts +166 -0
- package/test/integration/ConnectionManager.test.ts +291 -0
- package/test/integration/DhtNodeExternalAPI.test.ts +43 -0
- package/test/integration/DhtPeer.test.ts +73 -0
- package/test/integration/DhtRpc.test.ts +131 -0
- package/test/integration/DhtWithMockConnectionLatencies.test.ts +46 -0
- package/test/integration/DhtWithMockConnections.test.ts +46 -0
- package/test/integration/DhtWithRealConnectionLatencies.test.ts +47 -0
- package/test/integration/Layer1-scale.test.ts +200 -0
- package/test/integration/MigrateData.test.ts +203 -0
- package/test/integration/Mock-Layer1-Layer0.test.ts +106 -0
- package/test/integration/MultipleEntryPointJoining.test.ts +105 -0
- package/test/integration/RecursiveFind.test.ts +50 -0
- package/test/integration/RemoteRouter.test.ts +83 -0
- package/test/integration/RemoteStore.test.ts +66 -0
- package/test/integration/RouteMessage.test.ts +254 -0
- package/test/integration/RpcErrors.test.ts +153 -0
- package/test/integration/ScaleDownDht.test.ts +66 -0
- package/test/integration/SimultaneousConnections.test.ts +308 -0
- package/test/integration/Store.test.ts +72 -0
- package/test/integration/StoreAndDelete.test.ts +93 -0
- package/test/integration/StoreOnDhtWithTwoNodes.test.ts +71 -0
- package/test/integration/WebRtcConnectionManagement.test.ts +205 -0
- package/test/integration/WebRtcConnectorRpc.test.ts +145 -0
- package/test/integration/WebSocket.test.ts +64 -0
- package/test/integration/WebSocketConnectionManagement.test.ts +131 -0
- package/test/integration/WebSocketConnectorRpc.test.ts +86 -0
- package/test/kademlia-simulation/data/nodeids.json +13002 -0
- package/test/kademlia-simulation/data/orderedneighbors.json +1001 -0
- package/test/unit/AddressTools.test.ts +40 -0
- package/test/unit/DuplicateDetector.test.ts +29 -0
- package/test/unit/LocalDataStore.test.ts +107 -0
- package/test/unit/PeerID.test.ts +22 -0
- package/test/unit/ProtobufMessage.test.ts +21 -0
- package/test/unit/RandomContactList.test.ts +87 -0
- package/test/unit/RecursiveFinder.test.ts +112 -0
- package/test/unit/Router.test.ts +124 -0
- package/test/unit/SortedContactList.test.ts +127 -0
- package/test/unit/UUID.test.ts +49 -0
- package/test/unit/WebSocketServer.test.ts +42 -0
- package/test/utils/mock/RecursiveFinder.ts +19 -0
- package/test/utils/mock/Router.ts +53 -0
- package/test/utils/mock/Transport.ts +26 -0
- package/test/utils/utils.ts +311 -0
- package/tsconfig.browser.json +15 -0
- package/tsconfig.jest.json +19 -0
- package/tsconfig.json +3 -0
- package/tsconfig.node.json +18 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Message } from '../../proto/packages/dht/protos/DhtRpc'
|
|
2
|
+
|
|
3
|
+
type QueueEntry = [timeStamp: number, value: string, senderId: string, message?: Message]
|
|
4
|
+
|
|
5
|
+
export class DuplicateDetector {
|
|
6
|
+
|
|
7
|
+
private values: Set<string> = new Set()
|
|
8
|
+
private queue: Array<QueueEntry> = []
|
|
9
|
+
private maxAge: number
|
|
10
|
+
private maxNumberOfValues: number
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
maxNumberOfValues: number,
|
|
14
|
+
maxAgeInSeconds: number
|
|
15
|
+
) {
|
|
16
|
+
this.maxNumberOfValues = maxNumberOfValues
|
|
17
|
+
this.maxAge = maxAgeInSeconds * 1000
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public add(value: string, senderId: string, message?: Message): void {
|
|
21
|
+
this.values.add(value)
|
|
22
|
+
if (message) {
|
|
23
|
+
this.queue.push([Date.now(), value, senderId, message])
|
|
24
|
+
} else {
|
|
25
|
+
this.queue.push([Date.now(), value, senderId])
|
|
26
|
+
}
|
|
27
|
+
this.cleanUp()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public isMostLikelyDuplicate(value: string): boolean {
|
|
31
|
+
return this.values.has(value)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private cleanUp(): void {
|
|
35
|
+
const currentTime = Date.now()
|
|
36
|
+
|
|
37
|
+
while (this.queue.length > 0 && (this.queue.length > this.maxNumberOfValues ||
|
|
38
|
+
(currentTime - this.queue[0][0]) > this.maxAge)) {
|
|
39
|
+
const oldestEntry = this.queue.shift()
|
|
40
|
+
this.values.delete(oldestEntry![1])
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public size(): number {
|
|
45
|
+
return this.values.size
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public clear(): void {
|
|
49
|
+
this.values.clear()
|
|
50
|
+
this.queue = []
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { PeerDescriptor, RouteMessageWrapper } from '../../proto/packages/dht/protos/DhtRpc'
|
|
2
|
+
import { v4 } from 'uuid'
|
|
3
|
+
import { DhtRpcOptions } from '../../rpc-protocol/DhtRpcOptions'
|
|
4
|
+
import {
|
|
5
|
+
isSamePeerDescriptor,
|
|
6
|
+
keyFromPeerDescriptor,
|
|
7
|
+
peerIdFromPeerDescriptor
|
|
8
|
+
} from '../../helpers/peerIdFromPeerDescriptor'
|
|
9
|
+
import { IRoutingServiceClient } from '../../proto/packages/dht/protos/DhtRpc.client'
|
|
10
|
+
import { Remote } from '../contact/Remote'
|
|
11
|
+
import { Logger } from '@streamr/utils'
|
|
12
|
+
|
|
13
|
+
const logger = new Logger(module)
|
|
14
|
+
|
|
15
|
+
export class RemoteRouter extends Remote<IRoutingServiceClient> {
|
|
16
|
+
|
|
17
|
+
async routeMessage(params: RouteMessageWrapper): Promise<boolean> {
|
|
18
|
+
const message: RouteMessageWrapper = {
|
|
19
|
+
destinationPeer: params.destinationPeer,
|
|
20
|
+
sourcePeer: params.sourcePeer,
|
|
21
|
+
previousPeer: params.previousPeer,
|
|
22
|
+
message: params.message,
|
|
23
|
+
requestId: params.requestId || v4(),
|
|
24
|
+
reachableThrough: params.reachableThrough || [],
|
|
25
|
+
routingPath: params.routingPath
|
|
26
|
+
}
|
|
27
|
+
const options: DhtRpcOptions = {
|
|
28
|
+
sourceDescriptor: params.previousPeer as PeerDescriptor,
|
|
29
|
+
targetDescriptor: this.peerDescriptor as PeerDescriptor,
|
|
30
|
+
timeout: 10000
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
logger.trace('calling dhtClient.routeMessage')
|
|
34
|
+
const ack = await this.client.routeMessage(message, options)
|
|
35
|
+
logger.trace('dhtClient.routeMessage returned')
|
|
36
|
+
// Success signal if sent to destination and error includes duplicate
|
|
37
|
+
if (
|
|
38
|
+
isSamePeerDescriptor(params.destinationPeer!, this.peerDescriptor)
|
|
39
|
+
&& ack.error.includes('duplicate')
|
|
40
|
+
) {
|
|
41
|
+
return true
|
|
42
|
+
} else if (ack.error!.length > 0) {
|
|
43
|
+
return false
|
|
44
|
+
}
|
|
45
|
+
} catch (err) {
|
|
46
|
+
const fromNode = params.previousPeer ?
|
|
47
|
+
peerIdFromPeerDescriptor(params.previousPeer) : keyFromPeerDescriptor(params.sourcePeer!)
|
|
48
|
+
logger.debug(`Failed to send routeMessage from ${fromNode} to ${this.peerId.toKey()} with: ${err}`)
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
return true
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async forwardMessage(params: RouteMessageWrapper): Promise<boolean> {
|
|
55
|
+
const message: RouteMessageWrapper = {
|
|
56
|
+
destinationPeer: params.destinationPeer,
|
|
57
|
+
sourcePeer: params.sourcePeer,
|
|
58
|
+
previousPeer: params.previousPeer,
|
|
59
|
+
message: params.message,
|
|
60
|
+
requestId: params.requestId || v4(),
|
|
61
|
+
reachableThrough: params.reachableThrough || [],
|
|
62
|
+
routingPath: params.routingPath
|
|
63
|
+
}
|
|
64
|
+
const options: DhtRpcOptions = {
|
|
65
|
+
sourceDescriptor: params.previousPeer as PeerDescriptor,
|
|
66
|
+
targetDescriptor: this.peerDescriptor as PeerDescriptor,
|
|
67
|
+
timeout: 10000
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
const ack = await this.client.forwardMessage(message, options)
|
|
71
|
+
if (ack.error!.length > 0) {
|
|
72
|
+
return false
|
|
73
|
+
}
|
|
74
|
+
} catch (err) {
|
|
75
|
+
const fromNode = params.previousPeer ?
|
|
76
|
+
keyFromPeerDescriptor(params.previousPeer) : keyFromPeerDescriptor(params.sourcePeer!)
|
|
77
|
+
|
|
78
|
+
logger.debug(
|
|
79
|
+
`Failed to send forwardMessage from ${fromNode} to ${this.peerId.toKey()} with: ${err}`
|
|
80
|
+
)
|
|
81
|
+
return false
|
|
82
|
+
}
|
|
83
|
+
return true
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async findRecursively(params: RouteMessageWrapper): Promise<boolean> {
|
|
87
|
+
const message: RouteMessageWrapper = {
|
|
88
|
+
destinationPeer: params.destinationPeer,
|
|
89
|
+
sourcePeer: params.sourcePeer,
|
|
90
|
+
previousPeer: params.previousPeer,
|
|
91
|
+
message: params.message,
|
|
92
|
+
requestId: params.requestId || v4(),
|
|
93
|
+
reachableThrough: params.reachableThrough || [],
|
|
94
|
+
routingPath: params.routingPath
|
|
95
|
+
}
|
|
96
|
+
const options: DhtRpcOptions = {
|
|
97
|
+
sourceDescriptor: params.previousPeer as PeerDescriptor,
|
|
98
|
+
targetDescriptor: this.peerDescriptor as PeerDescriptor,
|
|
99
|
+
timeout: 10000
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const ack = await this.client.findRecursively(message, options)
|
|
103
|
+
if (ack.error!.length > 0) {
|
|
104
|
+
logger.debug('Next hop responded with error ' + ack.error)
|
|
105
|
+
return false
|
|
106
|
+
}
|
|
107
|
+
} catch (err) {
|
|
108
|
+
const fromNode = params.previousPeer ? keyFromPeerDescriptor(params.previousPeer) : keyFromPeerDescriptor(params.sourcePeer!)
|
|
109
|
+
logger.debug(`Failed to send recursiveFind message from ${fromNode} to ${this.peerId.toKey()} with: ${err}`)
|
|
110
|
+
return false
|
|
111
|
+
}
|
|
112
|
+
return true
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { Message, PeerDescriptor, RouteMessageAck, RouteMessageWrapper } from '../../proto/packages/dht/protos/DhtRpc'
|
|
2
|
+
import { ServerCallContext } from '@protobuf-ts/runtime-rpc'
|
|
3
|
+
import { keyFromPeerDescriptor, peerIdFromPeerDescriptor } from '../../helpers/peerIdFromPeerDescriptor'
|
|
4
|
+
import { RoutingMode, RoutingSession, RoutingSessionEvents } from './RoutingSession'
|
|
5
|
+
import { Logger, raceEvents3 } from '@streamr/utils'
|
|
6
|
+
import { RoutingRpcCommunicator } from '../../transport/RoutingRpcCommunicator'
|
|
7
|
+
import { PeerID, PeerIDKey } from '../../helpers/PeerID'
|
|
8
|
+
import { DuplicateDetector } from './DuplicateDetector'
|
|
9
|
+
import { ConnectionManager } from '../../connection/ConnectionManager'
|
|
10
|
+
import { DhtPeer } from '../DhtPeer'
|
|
11
|
+
import { v4 } from 'uuid'
|
|
12
|
+
import { IRoutingService } from '../../proto/packages/dht/protos/DhtRpc.server'
|
|
13
|
+
|
|
14
|
+
export const createRouteMessageAck = (routedMessage: RouteMessageWrapper, error?: string): RouteMessageAck => {
|
|
15
|
+
const ack: RouteMessageAck = {
|
|
16
|
+
requestId: routedMessage.requestId,
|
|
17
|
+
destinationPeer: routedMessage.sourcePeer,
|
|
18
|
+
sourcePeer: routedMessage.destinationPeer,
|
|
19
|
+
error: error ? error : ''
|
|
20
|
+
}
|
|
21
|
+
return ack
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export enum RoutingErrors {
|
|
25
|
+
NO_CANDIDATES_FOUND = 'No routing candidates found',
|
|
26
|
+
STOPPED = 'DhtNode Stopped'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface RouterConfig {
|
|
30
|
+
rpcCommunicator: RoutingRpcCommunicator
|
|
31
|
+
ownPeerDescriptor: PeerDescriptor
|
|
32
|
+
ownPeerId: PeerID
|
|
33
|
+
connections: Map<PeerIDKey, DhtPeer>
|
|
34
|
+
addContact: (contact: PeerDescriptor, setActive?: boolean) => void
|
|
35
|
+
serviceId: string
|
|
36
|
+
connectionManager?: ConnectionManager
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface ForwardingTableEntry {
|
|
40
|
+
timeout: NodeJS.Timeout
|
|
41
|
+
peerDescriptors: PeerDescriptor[]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface IRouterFunc {
|
|
45
|
+
doRouteMessage(routedMessage: RouteMessageWrapper, mode: RoutingMode, excludedPeer?: PeerDescriptor): RouteMessageAck
|
|
46
|
+
send(msg: Message, reachableThrough: PeerDescriptor[]): Promise<void>
|
|
47
|
+
checkDuplicate(messageId: string): boolean
|
|
48
|
+
addToDuplicateDetector(messageId: string, senderId: string, message?: Message): void
|
|
49
|
+
addRoutingSession(session: RoutingSession): void
|
|
50
|
+
removeRoutingSession(sessionId: string): void
|
|
51
|
+
stop(): void
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface IRouter extends Omit<IRoutingService, 'findRecursively'>, IRouterFunc {}
|
|
55
|
+
|
|
56
|
+
const logger = new Logger(module)
|
|
57
|
+
|
|
58
|
+
export class Router implements IRouter {
|
|
59
|
+
private readonly rpcCommunicator: RoutingRpcCommunicator
|
|
60
|
+
private readonly ownPeerDescriptor: PeerDescriptor
|
|
61
|
+
private readonly ownPeerId: PeerID
|
|
62
|
+
private readonly connections: Map<PeerIDKey, DhtPeer>
|
|
63
|
+
private readonly addContact: (contact: PeerDescriptor, setActive?: boolean) => void
|
|
64
|
+
private readonly serviceId: string
|
|
65
|
+
private readonly connectionManager?: ConnectionManager
|
|
66
|
+
private readonly forwardingTable: Map<string, ForwardingTableEntry> = new Map()
|
|
67
|
+
private ongoingRoutingSessions: Map<string, RoutingSession> = new Map()
|
|
68
|
+
private readonly routerDuplicateDetector: DuplicateDetector = new DuplicateDetector(100000, 100)
|
|
69
|
+
private stopped = false
|
|
70
|
+
|
|
71
|
+
constructor(config: RouterConfig) {
|
|
72
|
+
this.rpcCommunicator = config.rpcCommunicator
|
|
73
|
+
this.ownPeerDescriptor = config.ownPeerDescriptor
|
|
74
|
+
this.ownPeerId = config.ownPeerId
|
|
75
|
+
this.connections = config.connections
|
|
76
|
+
this.addContact = config.addContact
|
|
77
|
+
this.serviceId = config.serviceId
|
|
78
|
+
this.connectionManager = config.connectionManager
|
|
79
|
+
this.rpcCommunicator.registerRpcMethod(RouteMessageWrapper, RouteMessageAck, 'forwardMessage',
|
|
80
|
+
(forwardMessage: RouteMessageWrapper, context) => this.forwardMessage(forwardMessage, context))
|
|
81
|
+
this.rpcCommunicator.registerRpcMethod(RouteMessageWrapper, RouteMessageAck, 'routeMessage',
|
|
82
|
+
(routedMessage: RouteMessageWrapper, context) => this.routeMessage(routedMessage, context))
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public async send(msg: Message, reachableThrough: PeerDescriptor[]): Promise<void> {
|
|
86
|
+
msg.sourceDescriptor = this.ownPeerDescriptor
|
|
87
|
+
const targetPeerDescriptor = msg.targetDescriptor!
|
|
88
|
+
const forwardingEntry = this.forwardingTable.get(keyFromPeerDescriptor(targetPeerDescriptor))
|
|
89
|
+
if (forwardingEntry && forwardingEntry.peerDescriptors.length > 0) {
|
|
90
|
+
const forwardingPeer = forwardingEntry.peerDescriptors[0]
|
|
91
|
+
const forwardedMessage: RouteMessageWrapper = {
|
|
92
|
+
message: msg,
|
|
93
|
+
requestId: v4(),
|
|
94
|
+
destinationPeer: forwardingPeer,
|
|
95
|
+
sourcePeer: this.ownPeerDescriptor!,
|
|
96
|
+
reachableThrough,
|
|
97
|
+
routingPath: []
|
|
98
|
+
}
|
|
99
|
+
this.doRouteMessage(forwardedMessage, RoutingMode.FORWARD)
|
|
100
|
+
} else {
|
|
101
|
+
const routedMessage: RouteMessageWrapper = {
|
|
102
|
+
message: msg,
|
|
103
|
+
requestId: v4(),
|
|
104
|
+
destinationPeer: targetPeerDescriptor,
|
|
105
|
+
sourcePeer: this.ownPeerDescriptor!,
|
|
106
|
+
reachableThrough,
|
|
107
|
+
routingPath: []
|
|
108
|
+
}
|
|
109
|
+
this.doRouteMessage(routedMessage, RoutingMode.ROUTE)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public doRouteMessage(routedMessage: RouteMessageWrapper, mode = RoutingMode.ROUTE, excludedPeer?: PeerDescriptor): RouteMessageAck {
|
|
114
|
+
if (this.stopped) {
|
|
115
|
+
return createRouteMessageAck(routedMessage, RoutingErrors.STOPPED)
|
|
116
|
+
}
|
|
117
|
+
logger.trace(`Peer ${this.ownPeerId.value} routing message ${routedMessage.requestId}
|
|
118
|
+
from ${routedMessage.sourcePeer?.kademliaId} to ${routedMessage.destinationPeer?.kademliaId}`)
|
|
119
|
+
routedMessage.routingPath.push(this.ownPeerDescriptor!)
|
|
120
|
+
const session = this.createRoutingSession(routedMessage, mode, excludedPeer)
|
|
121
|
+
this.addRoutingSession(session)
|
|
122
|
+
try {
|
|
123
|
+
// eslint-disable-next-line promise/catch-or-return
|
|
124
|
+
logger.trace('starting to raceEvents from routingSession: ' + session.sessionId)
|
|
125
|
+
raceEvents3<RoutingSessionEvents>(session, ['routingSucceeded', 'partialSuccess', 'routingFailed', 'stopped', 'noCandidatesFound'], 10000)
|
|
126
|
+
.then(() => {
|
|
127
|
+
logger.trace('raceEvents ended from routingSession: ' + session.sessionId)
|
|
128
|
+
this.removeRoutingSession(session.sessionId)
|
|
129
|
+
})
|
|
130
|
+
.catch(() => {
|
|
131
|
+
logger.trace('raceEvents timed out for routingSession ' + session.sessionId)
|
|
132
|
+
this.removeRoutingSession(session.sessionId)
|
|
133
|
+
})
|
|
134
|
+
session.start()
|
|
135
|
+
} catch (e) {
|
|
136
|
+
if (peerIdFromPeerDescriptor(routedMessage.sourcePeer!).equals(this.ownPeerId!)) {
|
|
137
|
+
logger.warn(
|
|
138
|
+
`Failed to send (routeMessage: ${this.serviceId}) to ${keyFromPeerDescriptor(routedMessage.destinationPeer!)}: ${e}`
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
return createRouteMessageAck(routedMessage, RoutingErrors.NO_CANDIDATES_FOUND)
|
|
142
|
+
}
|
|
143
|
+
return createRouteMessageAck(routedMessage)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private createRoutingSession(routedMessage: RouteMessageWrapper, mode: RoutingMode, excludedPeer?: PeerDescriptor): RoutingSession {
|
|
147
|
+
const excludedPeers = routedMessage.routingPath.map((descriptor) => peerIdFromPeerDescriptor(descriptor))
|
|
148
|
+
if (excludedPeer) {
|
|
149
|
+
excludedPeers.push(peerIdFromPeerDescriptor(excludedPeer))
|
|
150
|
+
}
|
|
151
|
+
logger.trace(' routing session created with connections: ' + this.connections.size )
|
|
152
|
+
return new RoutingSession(
|
|
153
|
+
this.rpcCommunicator,
|
|
154
|
+
this.ownPeerDescriptor!,
|
|
155
|
+
routedMessage,
|
|
156
|
+
this.connections,
|
|
157
|
+
this.ownPeerId!.equals(peerIdFromPeerDescriptor(routedMessage.sourcePeer!)) ? 2 : 1,
|
|
158
|
+
mode,
|
|
159
|
+
undefined,
|
|
160
|
+
excludedPeers
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
public checkDuplicate(messageId: string): boolean {
|
|
165
|
+
return this.routerDuplicateDetector.isMostLikelyDuplicate(messageId)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
public addToDuplicateDetector(messageId: string, senderId: string, message?: Message): void {
|
|
169
|
+
this.routerDuplicateDetector.add(messageId, senderId, message)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
public addRoutingSession(session: RoutingSession): void {
|
|
173
|
+
this.ongoingRoutingSessions.set(session.sessionId, session)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
public removeRoutingSession(sessionId: string): void {
|
|
177
|
+
this.ongoingRoutingSessions.delete(sessionId)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
public stop(): void {
|
|
181
|
+
this.stopped = true
|
|
182
|
+
this.ongoingRoutingSessions.forEach((session, _id) => {
|
|
183
|
+
session.stop()
|
|
184
|
+
})
|
|
185
|
+
this.ongoingRoutingSessions.clear()
|
|
186
|
+
this.forwardingTable.forEach((entry) => {
|
|
187
|
+
clearTimeout(entry.timeout)
|
|
188
|
+
})
|
|
189
|
+
this.forwardingTable.clear()
|
|
190
|
+
this.routerDuplicateDetector.clear()
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// IRoutingService method
|
|
194
|
+
async routeMessage(routedMessage: RouteMessageWrapper, _context: ServerCallContext): Promise<RouteMessageAck> {
|
|
195
|
+
if (this.stopped) {
|
|
196
|
+
return createRouteMessageAck(routedMessage, 'routeMessage() service is not running')
|
|
197
|
+
} else if (this.routerDuplicateDetector.isMostLikelyDuplicate(routedMessage.requestId)) {
|
|
198
|
+
logger.trace(`Peer ${this.ownPeerId?.value} routing message ${routedMessage.requestId}
|
|
199
|
+
from ${routedMessage.sourcePeer!.kademliaId} to ${routedMessage.destinationPeer!.kademliaId} is likely a duplicate`)
|
|
200
|
+
return createRouteMessageAck(routedMessage, 'message given to routeMessage() service is likely a duplicate')
|
|
201
|
+
}
|
|
202
|
+
logger.trace(`Processing received routeMessage ${routedMessage.requestId}`)
|
|
203
|
+
this.addContact(routedMessage.sourcePeer!, true)
|
|
204
|
+
this.addToDuplicateDetector(routedMessage.requestId, keyFromPeerDescriptor(routedMessage.sourcePeer!))
|
|
205
|
+
if (this.ownPeerId!.equals(peerIdFromPeerDescriptor(routedMessage.destinationPeer!))) {
|
|
206
|
+
logger.trace(`${this.ownPeerDescriptor.nodeName} routing message targeted to self ${routedMessage.requestId}`)
|
|
207
|
+
this.setForwardingEntries(routedMessage)
|
|
208
|
+
this.connectionManager?.handleMessage(routedMessage.message!)
|
|
209
|
+
return createRouteMessageAck(routedMessage)
|
|
210
|
+
} else {
|
|
211
|
+
return this.doRouteMessage(routedMessage)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
private setForwardingEntries(routedMessage: RouteMessageWrapper): void {
|
|
216
|
+
const reachableThroughWithoutSelf = routedMessage.reachableThrough.filter((peer) => {
|
|
217
|
+
return !peerIdFromPeerDescriptor(peer).equals(this.ownPeerId!)
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
if (reachableThroughWithoutSelf.length > 0) {
|
|
221
|
+
const sourceKey = keyFromPeerDescriptor(routedMessage.sourcePeer!)
|
|
222
|
+
if (this.forwardingTable.has(sourceKey)) {
|
|
223
|
+
const oldEntry = this.forwardingTable.get(sourceKey)
|
|
224
|
+
clearTimeout(oldEntry!.timeout)
|
|
225
|
+
this.forwardingTable.delete(sourceKey)
|
|
226
|
+
}
|
|
227
|
+
const forwardingEntry: ForwardingTableEntry = {
|
|
228
|
+
peerDescriptors: reachableThroughWithoutSelf,
|
|
229
|
+
timeout: setTimeout(() => {
|
|
230
|
+
this.forwardingTable.delete(sourceKey)
|
|
231
|
+
}, 10000)
|
|
232
|
+
}
|
|
233
|
+
this.forwardingTable.set(sourceKey, forwardingEntry)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// IRoutingService method
|
|
238
|
+
async forwardMessage(forwardMessage: RouteMessageWrapper, _context: ServerCallContext): Promise<RouteMessageAck> {
|
|
239
|
+
if (this.stopped) {
|
|
240
|
+
return createRouteMessageAck(forwardMessage, 'forwardMessage() service is not running')
|
|
241
|
+
} else if (this.routerDuplicateDetector.isMostLikelyDuplicate(forwardMessage.requestId)) {
|
|
242
|
+
logger.trace(`Peer ${this.ownPeerId.value} forwarding message ${forwardMessage.requestId}
|
|
243
|
+
from ${forwardMessage.sourcePeer?.kademliaId} to ${forwardMessage.destinationPeer?.kademliaId} is likely a duplicate`)
|
|
244
|
+
return createRouteMessageAck(forwardMessage, 'message given to forwardMessage() service is likely a duplicate')
|
|
245
|
+
}
|
|
246
|
+
logger.trace(`Processing received forward routeMessage ${forwardMessage.requestId}`)
|
|
247
|
+
this.addContact(forwardMessage.sourcePeer!, true)
|
|
248
|
+
this.addToDuplicateDetector(forwardMessage.requestId, keyFromPeerDescriptor(forwardMessage.sourcePeer!))
|
|
249
|
+
if (this.ownPeerId.equals(peerIdFromPeerDescriptor(forwardMessage.destinationPeer!))) {
|
|
250
|
+
return this.forwardToDestination(forwardMessage)
|
|
251
|
+
} else {
|
|
252
|
+
return this.doRouteMessage(forwardMessage, RoutingMode.FORWARD)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private forwardToDestination(routedMessage: RouteMessageWrapper): RouteMessageAck {
|
|
257
|
+
logger.trace(`Peer ${this.ownPeerId.value} forwarding found message targeted to self ${routedMessage.requestId}`)
|
|
258
|
+
const forwardedMessage = routedMessage.message!
|
|
259
|
+
if (this.ownPeerId!.equals(peerIdFromPeerDescriptor(forwardedMessage.targetDescriptor!))) {
|
|
260
|
+
this.connectionManager?.handleMessage(forwardedMessage!)
|
|
261
|
+
return createRouteMessageAck(routedMessage)
|
|
262
|
+
}
|
|
263
|
+
return this.doRouteMessage({ ...routedMessage, destinationPeer: forwardedMessage.targetDescriptor })
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { PeerDescriptor } from '../../exports'
|
|
2
|
+
import { DhtPeer } from '../DhtPeer'
|
|
3
|
+
import { SortedContactList } from '../contact/SortedContactList'
|
|
4
|
+
import { PeerID, PeerIDKey } from '../../helpers/PeerID'
|
|
5
|
+
import { Logger } from '@streamr/utils'
|
|
6
|
+
import EventEmitter from 'eventemitter3'
|
|
7
|
+
import { v4 } from 'uuid'
|
|
8
|
+
import { RouteMessageWrapper } from '../../proto/packages/dht/protos/DhtRpc'
|
|
9
|
+
import { RemoteRouter } from './RemoteRouter'
|
|
10
|
+
import { RoutingRpcCommunicator } from '../../transport/RoutingRpcCommunicator'
|
|
11
|
+
import { RoutingServiceClient } from '../../proto/packages/dht/protos/DhtRpc.client'
|
|
12
|
+
import { toProtoRpcClient } from '@streamr/proto-rpc'
|
|
13
|
+
|
|
14
|
+
const logger = new Logger(module)
|
|
15
|
+
|
|
16
|
+
const MAX_FAILED_HOPS = 2
|
|
17
|
+
|
|
18
|
+
export interface RoutingSessionEvents {
|
|
19
|
+
// This event is emitted when a peer responds with a success ack
|
|
20
|
+
// to routeMessage call
|
|
21
|
+
routingSucceeded: (sessionId: string) => void
|
|
22
|
+
partialSuccess: (sessionId: string) => void
|
|
23
|
+
|
|
24
|
+
// This event is emitted when all the candidates have been gone
|
|
25
|
+
// through, and none of them responds with a success ack
|
|
26
|
+
routingFailed: (sessionId: string) => void
|
|
27
|
+
stopped: (sessionId: string) => void
|
|
28
|
+
noCandidatesFound: (sessionId: string) => void
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export enum RoutingMode { ROUTE, FORWARD, RECURSIVE_FIND }
|
|
32
|
+
|
|
33
|
+
export class RoutingSession extends EventEmitter<RoutingSessionEvents> {
|
|
34
|
+
|
|
35
|
+
public readonly sessionId = v4()
|
|
36
|
+
private readonly rpcCommunicator: RoutingRpcCommunicator
|
|
37
|
+
private ongoingRequests: Set<PeerIDKey> = new Set()
|
|
38
|
+
private contactList: SortedContactList<RemoteRouter>
|
|
39
|
+
private readonly ownPeerDescriptor: PeerDescriptor
|
|
40
|
+
private readonly messageToRoute: RouteMessageWrapper
|
|
41
|
+
private connections: Map<PeerIDKey, DhtPeer>
|
|
42
|
+
private readonly parallelism: number
|
|
43
|
+
private failedHopCounter = 0
|
|
44
|
+
private successfulHopCounter = 0
|
|
45
|
+
private readonly mode: RoutingMode = RoutingMode.ROUTE
|
|
46
|
+
private stopped = false
|
|
47
|
+
|
|
48
|
+
constructor(
|
|
49
|
+
rpcCommunicator: RoutingRpcCommunicator,
|
|
50
|
+
ownPeerDescriptor: PeerDescriptor,
|
|
51
|
+
messageToRoute: RouteMessageWrapper,
|
|
52
|
+
connections: Map<PeerIDKey, DhtPeer>,
|
|
53
|
+
parallelism: number,
|
|
54
|
+
mode: RoutingMode = RoutingMode.ROUTE,
|
|
55
|
+
destinationId?: Uint8Array,
|
|
56
|
+
excludedPeerIDs?: PeerID[]
|
|
57
|
+
) {
|
|
58
|
+
super()
|
|
59
|
+
this.rpcCommunicator = rpcCommunicator
|
|
60
|
+
this.ownPeerDescriptor = ownPeerDescriptor
|
|
61
|
+
this.messageToRoute = messageToRoute
|
|
62
|
+
this.connections = connections
|
|
63
|
+
this.parallelism = parallelism
|
|
64
|
+
this.mode = mode
|
|
65
|
+
const previousId = messageToRoute.previousPeer ? PeerID.fromValue(messageToRoute.previousPeer.kademliaId) : undefined
|
|
66
|
+
this.contactList = new SortedContactList(
|
|
67
|
+
destinationId ? PeerID.fromValue(destinationId) : PeerID.fromValue(this.messageToRoute!.destinationPeer!.kademliaId),
|
|
68
|
+
10000,
|
|
69
|
+
undefined,
|
|
70
|
+
true,
|
|
71
|
+
previousId,
|
|
72
|
+
excludedPeerIDs
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private onRequestFailed = (peerId: PeerID) => {
|
|
77
|
+
logger.trace('onRequestFailed() sessionId: ' + this.sessionId)
|
|
78
|
+
if (this.stopped) {
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
if (this.ongoingRequests.has(peerId.toKey())) {
|
|
82
|
+
this.ongoingRequests.delete(peerId.toKey())
|
|
83
|
+
}
|
|
84
|
+
const contacts = this.findMoreContacts()
|
|
85
|
+
if (contacts.length < 1 && this.ongoingRequests.size < 1) {
|
|
86
|
+
logger.trace('routing failed, emitting routingFailed sessionId: ' + this.sessionId)
|
|
87
|
+
this.stopped = true
|
|
88
|
+
this.emitFailure()
|
|
89
|
+
} else {
|
|
90
|
+
this.failedHopCounter += 1
|
|
91
|
+
logger.trace('routing failed, retrying to route sessionId: ' + this.sessionId + ' failedHopCounter: ' + this.failedHopCounter)
|
|
92
|
+
this.sendMoreRequests(contacts)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private emitFailure = () => {
|
|
97
|
+
if (this.successfulHopCounter >= 1) {
|
|
98
|
+
this.emit('partialSuccess', this.sessionId)
|
|
99
|
+
|
|
100
|
+
} else {
|
|
101
|
+
this.emit('routingFailed', this.sessionId)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private onRequestSucceeded = (_peerId: PeerID) => {
|
|
106
|
+
logger.trace('onRequestSucceeded() sessionId: ' + this.sessionId)
|
|
107
|
+
if (this.stopped) {
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
this.successfulHopCounter += 1
|
|
111
|
+
const contacts = this.findMoreContacts()
|
|
112
|
+
if (this.successfulHopCounter >= this.parallelism || contacts.length < 1) {
|
|
113
|
+
this.stopped = true
|
|
114
|
+
this.emit('routingSucceeded', this.sessionId)
|
|
115
|
+
} else if (contacts.length > 0 && this.ongoingRequests.size < 1) {
|
|
116
|
+
this.sendMoreRequests(contacts)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private sendRouteMessageRequest = async (contact: RemoteRouter): Promise<boolean> => {
|
|
121
|
+
logger.trace('sendRouteMessageRequest() sessionId: ' + this.sessionId)
|
|
122
|
+
logger.trace(`Sending routeMessage request from ${this.ownPeerDescriptor.kademliaId} to contact: ${contact.getPeerId()}`)
|
|
123
|
+
this.contactList.setContacted(contact.getPeerId())
|
|
124
|
+
this.ongoingRequests.add(contact.getPeerId().toKey())
|
|
125
|
+
if (this.mode === RoutingMode.FORWARD) {
|
|
126
|
+
return contact.forwardMessage({
|
|
127
|
+
...this.messageToRoute,
|
|
128
|
+
previousPeer: this.ownPeerDescriptor
|
|
129
|
+
})
|
|
130
|
+
} else if (this.mode === RoutingMode.RECURSIVE_FIND) {
|
|
131
|
+
return contact.findRecursively({
|
|
132
|
+
...this.messageToRoute,
|
|
133
|
+
previousPeer: this.ownPeerDescriptor
|
|
134
|
+
})
|
|
135
|
+
} else {
|
|
136
|
+
return contact.routeMessage({
|
|
137
|
+
...this.messageToRoute,
|
|
138
|
+
previousPeer: this.ownPeerDescriptor
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private findMoreContacts = (): RemoteRouter[] => {
|
|
144
|
+
logger.trace('findMoreContacts() sessionId: ' + this.sessionId)
|
|
145
|
+
// the contents of the connections might have changed between the rounds
|
|
146
|
+
// addContacts() will only add new contacts that were not there yet
|
|
147
|
+
const contacts = Array.from(this.connections.values())
|
|
148
|
+
.map((contact) => {
|
|
149
|
+
return new RemoteRouter(
|
|
150
|
+
this.ownPeerDescriptor,
|
|
151
|
+
contact.getPeerDescriptor(),
|
|
152
|
+
toProtoRpcClient(new RoutingServiceClient(this.rpcCommunicator!.getRpcClientTransport())),
|
|
153
|
+
contact.getServiceId()
|
|
154
|
+
)
|
|
155
|
+
})
|
|
156
|
+
this.contactList.addContacts(contacts)
|
|
157
|
+
return this.contactList.getUncontactedContacts(this.parallelism)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
public getClosestContacts = (limit: number): PeerDescriptor[] => {
|
|
161
|
+
const contacts = this.contactList.getClosestContacts(limit)
|
|
162
|
+
return contacts.map((contact) => contact.getPeerDescriptor())
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private sendMoreRequests = (uncontacted: RemoteRouter[]) => {
|
|
166
|
+
logger.trace('sendMoreRequests() sessionId: ' + this.sessionId)
|
|
167
|
+
if (this.stopped) {
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
if (uncontacted.length < 1) {
|
|
171
|
+
this.emitFailure()
|
|
172
|
+
return
|
|
173
|
+
}
|
|
174
|
+
if (this.failedHopCounter >= MAX_FAILED_HOPS) {
|
|
175
|
+
logger.trace(`Stopping routing after ${MAX_FAILED_HOPS} failed attempts for sessionId: ${this.sessionId}`)
|
|
176
|
+
this.emitFailure()
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
while (this.ongoingRequests.size < this.parallelism && uncontacted.length > 0) {
|
|
180
|
+
if (this.stopped) {
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
const nextPeer = uncontacted.shift()
|
|
184
|
+
logger.trace('sendRouteMessageRequest')
|
|
185
|
+
// eslint-disable-next-line promise/catch-or-return
|
|
186
|
+
this.sendRouteMessageRequest(nextPeer!)
|
|
187
|
+
.then((succeeded) => {
|
|
188
|
+
if (succeeded) {
|
|
189
|
+
this.onRequestSucceeded(nextPeer!.getPeerId())
|
|
190
|
+
} else {
|
|
191
|
+
this.onRequestFailed(nextPeer!.getPeerId())
|
|
192
|
+
}
|
|
193
|
+
}).catch((e) => {
|
|
194
|
+
logger.error(e)
|
|
195
|
+
}).finally(() => {
|
|
196
|
+
logger.trace('sendRouteMessageRequest returned')
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
public start(): void {
|
|
202
|
+
logger.trace('start() sessionId: ' + this.sessionId)
|
|
203
|
+
const contacts = this.findMoreContacts()
|
|
204
|
+
if (contacts.length < 1) {
|
|
205
|
+
logger.trace('start() throwing noCandidatesFound sessionId: ' + this.sessionId)
|
|
206
|
+
|
|
207
|
+
this.stopped = true
|
|
208
|
+
this.emit('noCandidatesFound', this.sessionId)
|
|
209
|
+
throw new Error('noCandidatesFound ' + this.sessionId)
|
|
210
|
+
}
|
|
211
|
+
this.sendMoreRequests(contacts)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
public stop(): void {
|
|
215
|
+
this.stopped = true
|
|
216
|
+
this.contactList.stop()
|
|
217
|
+
|
|
218
|
+
this.emit('stopped', this.sessionId)
|
|
219
|
+
this.removeAllListeners()
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
}
|