@streamr/trackerless-network 103.1.2 → 103.2.0-experiment.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 (172) hide show
  1. package/dist/exports.cjs +4489 -0
  2. package/dist/exports.cjs.map +1 -0
  3. package/dist/exports.d.ts +1391 -0
  4. package/dist/exports.js +4477 -0
  5. package/dist/exports.js.map +1 -0
  6. package/package.json +25 -18
  7. package/dist/generated/google/protobuf/any.d.ts +0 -180
  8. package/dist/generated/google/protobuf/any.js +0 -155
  9. package/dist/generated/google/protobuf/any.js.map +0 -1
  10. package/dist/generated/google/protobuf/empty.d.ts +0 -31
  11. package/dist/generated/google/protobuf/empty.js +0 -45
  12. package/dist/generated/google/protobuf/empty.js.map +0 -1
  13. package/dist/generated/google/protobuf/timestamp.d.ts +0 -156
  14. package/dist/generated/google/protobuf/timestamp.js +0 -136
  15. package/dist/generated/google/protobuf/timestamp.js.map +0 -1
  16. package/dist/generated/packages/dht/protos/DhtRpc.client.d.ts +0 -371
  17. package/dist/generated/packages/dht/protos/DhtRpc.client.js +0 -292
  18. package/dist/generated/packages/dht/protos/DhtRpc.client.js.map +0 -1
  19. package/dist/generated/packages/dht/protos/DhtRpc.d.ts +0 -1031
  20. package/dist/generated/packages/dht/protos/DhtRpc.js +0 -702
  21. package/dist/generated/packages/dht/protos/DhtRpc.js.map +0 -1
  22. package/dist/generated/packages/dht/protos/DhtRpc.server.d.ts +0 -168
  23. package/dist/generated/packages/dht/protos/DhtRpc.server.js +0 -3
  24. package/dist/generated/packages/dht/protos/DhtRpc.server.js.map +0 -1
  25. package/dist/generated/packages/proto-rpc/protos/ProtoRpc.d.ts +0 -87
  26. package/dist/generated/packages/proto-rpc/protos/ProtoRpc.js +0 -66
  27. package/dist/generated/packages/proto-rpc/protos/ProtoRpc.js.map +0 -1
  28. package/dist/generated/packages/trackerless-network/protos/NetworkRpc.client.d.ts +0 -237
  29. package/dist/generated/packages/trackerless-network/protos/NetworkRpc.client.js +0 -190
  30. package/dist/generated/packages/trackerless-network/protos/NetworkRpc.client.js.map +0 -1
  31. package/dist/generated/packages/trackerless-network/protos/NetworkRpc.d.ts +0 -687
  32. package/dist/generated/packages/trackerless-network/protos/NetworkRpc.js +0 -479
  33. package/dist/generated/packages/trackerless-network/protos/NetworkRpc.js.map +0 -1
  34. package/dist/generated/packages/trackerless-network/protos/NetworkRpc.server.d.ts +0 -102
  35. package/dist/generated/packages/trackerless-network/protos/NetworkRpc.server.js +0 -3
  36. package/dist/generated/packages/trackerless-network/protos/NetworkRpc.server.js.map +0 -1
  37. package/dist/package.json +0 -56
  38. package/dist/src/ContentDeliveryManager.d.ts +0 -82
  39. package/dist/src/ContentDeliveryManager.js +0 -325
  40. package/dist/src/ContentDeliveryManager.js.map +0 -1
  41. package/dist/src/NetworkNode.d.ts +0 -44
  42. package/dist/src/NetworkNode.js +0 -98
  43. package/dist/src/NetworkNode.js.map +0 -1
  44. package/dist/src/NetworkStack.d.ts +0 -36
  45. package/dist/src/NetworkStack.js +0 -165
  46. package/dist/src/NetworkStack.js.map +0 -1
  47. package/dist/src/NodeInfoClient.d.ts +0 -9
  48. package/dist/src/NodeInfoClient.js +0 -21
  49. package/dist/src/NodeInfoClient.js.map +0 -1
  50. package/dist/src/NodeInfoRpcLocal.d.ts +0 -12
  51. package/dist/src/NodeInfoRpcLocal.js +0 -22
  52. package/dist/src/NodeInfoRpcLocal.js.map +0 -1
  53. package/dist/src/NodeInfoRpcRemote.d.ts +0 -6
  54. package/dist/src/NodeInfoRpcRemote.js +0 -11
  55. package/dist/src/NodeInfoRpcRemote.js.map +0 -1
  56. package/dist/src/StreamPartNetworkSplitAvoidance.d.ts +0 -18
  57. package/dist/src/StreamPartNetworkSplitAvoidance.js +0 -74
  58. package/dist/src/StreamPartNetworkSplitAvoidance.js.map +0 -1
  59. package/dist/src/StreamPartReconnect.d.ts +0 -11
  60. package/dist/src/StreamPartReconnect.js +0 -37
  61. package/dist/src/StreamPartReconnect.js.map +0 -1
  62. package/dist/src/content-delivery-layer/ContentDeliveryLayerNode.d.ts +0 -78
  63. package/dist/src/content-delivery-layer/ContentDeliveryLayerNode.js +0 -240
  64. package/dist/src/content-delivery-layer/ContentDeliveryLayerNode.js.map +0 -1
  65. package/dist/src/content-delivery-layer/ContentDeliveryRpcLocal.d.ts +0 -23
  66. package/dist/src/content-delivery-layer/ContentDeliveryRpcLocal.js +0 -40
  67. package/dist/src/content-delivery-layer/ContentDeliveryRpcLocal.js.map +0 -1
  68. package/dist/src/content-delivery-layer/ContentDeliveryRpcRemote.d.ts +0 -11
  69. package/dist/src/content-delivery-layer/ContentDeliveryRpcRemote.js +0 -38
  70. package/dist/src/content-delivery-layer/ContentDeliveryRpcRemote.js.map +0 -1
  71. package/dist/src/content-delivery-layer/DuplicateMessageDetector.d.ts +0 -55
  72. package/dist/src/content-delivery-layer/DuplicateMessageDetector.js +0 -159
  73. package/dist/src/content-delivery-layer/DuplicateMessageDetector.js.map +0 -1
  74. package/dist/src/content-delivery-layer/NodeList.d.ts +0 -26
  75. package/dist/src/content-delivery-layer/NodeList.js +0 -93
  76. package/dist/src/content-delivery-layer/NodeList.js.map +0 -1
  77. package/dist/src/content-delivery-layer/createContentDeliveryLayerNode.d.ts +0 -14
  78. package/dist/src/content-delivery-layer/createContentDeliveryLayerNode.js +0 -129
  79. package/dist/src/content-delivery-layer/createContentDeliveryLayerNode.js.map +0 -1
  80. package/dist/src/content-delivery-layer/formStreamPartDeliveryServiceId.d.ts +0 -3
  81. package/dist/src/content-delivery-layer/formStreamPartDeliveryServiceId.js +0 -9
  82. package/dist/src/content-delivery-layer/formStreamPartDeliveryServiceId.js.map +0 -1
  83. package/dist/src/content-delivery-layer/inspection/InspectSession.d.ts +0 -19
  84. package/dist/src/content-delivery-layer/inspection/InspectSession.js +0 -43
  85. package/dist/src/content-delivery-layer/inspection/InspectSession.js.map +0 -1
  86. package/dist/src/content-delivery-layer/inspection/Inspector.d.ts +0 -30
  87. package/dist/src/content-delivery-layer/inspection/Inspector.js +0 -75
  88. package/dist/src/content-delivery-layer/inspection/Inspector.js.map +0 -1
  89. package/dist/src/content-delivery-layer/neighbor-discovery/HandshakeRpcLocal.d.ts +0 -29
  90. package/dist/src/content-delivery-layer/neighbor-discovery/HandshakeRpcLocal.js +0 -106
  91. package/dist/src/content-delivery-layer/neighbor-discovery/HandshakeRpcLocal.js.map +0 -1
  92. package/dist/src/content-delivery-layer/neighbor-discovery/HandshakeRpcRemote.d.ts +0 -14
  93. package/dist/src/content-delivery-layer/neighbor-discovery/HandshakeRpcRemote.js +0 -55
  94. package/dist/src/content-delivery-layer/neighbor-discovery/HandshakeRpcRemote.js.map +0 -1
  95. package/dist/src/content-delivery-layer/neighbor-discovery/Handshaker.d.ts +0 -32
  96. package/dist/src/content-delivery-layer/neighbor-discovery/Handshaker.js +0 -149
  97. package/dist/src/content-delivery-layer/neighbor-discovery/Handshaker.js.map +0 -1
  98. package/dist/src/content-delivery-layer/neighbor-discovery/NeighborFinder.d.ts +0 -22
  99. package/dist/src/content-delivery-layer/neighbor-discovery/NeighborFinder.js +0 -64
  100. package/dist/src/content-delivery-layer/neighbor-discovery/NeighborFinder.js.map +0 -1
  101. package/dist/src/content-delivery-layer/neighbor-discovery/NeighborUpdateManager.d.ts +0 -27
  102. package/dist/src/content-delivery-layer/neighbor-discovery/NeighborUpdateManager.js +0 -47
  103. package/dist/src/content-delivery-layer/neighbor-discovery/NeighborUpdateManager.js.map +0 -1
  104. package/dist/src/content-delivery-layer/neighbor-discovery/NeighborUpdateRpcLocal.d.ts +0 -25
  105. package/dist/src/content-delivery-layer/neighbor-discovery/NeighborUpdateRpcLocal.js +0 -52
  106. package/dist/src/content-delivery-layer/neighbor-discovery/NeighborUpdateRpcLocal.js.map +0 -1
  107. package/dist/src/content-delivery-layer/neighbor-discovery/NeighborUpdateRpcRemote.d.ts +0 -11
  108. package/dist/src/content-delivery-layer/neighbor-discovery/NeighborUpdateRpcRemote.js +0 -31
  109. package/dist/src/content-delivery-layer/neighbor-discovery/NeighborUpdateRpcRemote.js.map +0 -1
  110. package/dist/src/content-delivery-layer/plumtree/PausedNeighbors.d.ts +0 -12
  111. package/dist/src/content-delivery-layer/plumtree/PausedNeighbors.js +0 -50
  112. package/dist/src/content-delivery-layer/plumtree/PausedNeighbors.js.map +0 -1
  113. package/dist/src/content-delivery-layer/plumtree/PlumtreeManager.d.ts +0 -37
  114. package/dist/src/content-delivery-layer/plumtree/PlumtreeManager.js +0 -141
  115. package/dist/src/content-delivery-layer/plumtree/PlumtreeManager.js.map +0 -1
  116. package/dist/src/content-delivery-layer/plumtree/PlumtreeRpcLocal.d.ts +0 -20
  117. package/dist/src/content-delivery-layer/plumtree/PlumtreeRpcLocal.js +0 -37
  118. package/dist/src/content-delivery-layer/plumtree/PlumtreeRpcLocal.js.map +0 -1
  119. package/dist/src/content-delivery-layer/plumtree/PlumtreeRpcRemote.d.ts +0 -8
  120. package/dist/src/content-delivery-layer/plumtree/PlumtreeRpcRemote.js +0 -26
  121. package/dist/src/content-delivery-layer/plumtree/PlumtreeRpcRemote.js.map +0 -1
  122. package/dist/src/content-delivery-layer/propagation/FifoMapWithTTL.d.ts +0 -29
  123. package/dist/src/content-delivery-layer/propagation/FifoMapWithTTL.js +0 -93
  124. package/dist/src/content-delivery-layer/propagation/FifoMapWithTTL.js.map +0 -1
  125. package/dist/src/content-delivery-layer/propagation/Propagation.d.ts +0 -35
  126. package/dist/src/content-delivery-layer/propagation/Propagation.js +0 -68
  127. package/dist/src/content-delivery-layer/propagation/Propagation.js.map +0 -1
  128. package/dist/src/content-delivery-layer/propagation/PropagationTaskStore.d.ts +0 -22
  129. package/dist/src/content-delivery-layer/propagation/PropagationTaskStore.js +0 -33
  130. package/dist/src/content-delivery-layer/propagation/PropagationTaskStore.js.map +0 -1
  131. package/dist/src/content-delivery-layer/proxy/ProxyClient.d.ts +0 -46
  132. package/dist/src/content-delivery-layer/proxy/ProxyClient.js +0 -214
  133. package/dist/src/content-delivery-layer/proxy/ProxyClient.js.map +0 -1
  134. package/dist/src/content-delivery-layer/proxy/ProxyConnectionRpcLocal.d.ts +0 -34
  135. package/dist/src/content-delivery-layer/proxy/ProxyConnectionRpcLocal.js +0 -72
  136. package/dist/src/content-delivery-layer/proxy/ProxyConnectionRpcLocal.js.map +0 -1
  137. package/dist/src/content-delivery-layer/proxy/ProxyConnectionRpcRemote.d.ts +0 -7
  138. package/dist/src/content-delivery-layer/proxy/ProxyConnectionRpcRemote.js +0 -27
  139. package/dist/src/content-delivery-layer/proxy/ProxyConnectionRpcRemote.js.map +0 -1
  140. package/dist/src/content-delivery-layer/temporary-connection/TemporaryConnectionRpcLocal.d.ts +0 -26
  141. package/dist/src/content-delivery-layer/temporary-connection/TemporaryConnectionRpcLocal.js +0 -45
  142. package/dist/src/content-delivery-layer/temporary-connection/TemporaryConnectionRpcLocal.js.map +0 -1
  143. package/dist/src/content-delivery-layer/temporary-connection/TemporaryConnectionRpcRemote.d.ts +0 -6
  144. package/dist/src/content-delivery-layer/temporary-connection/TemporaryConnectionRpcRemote.js +0 -31
  145. package/dist/src/content-delivery-layer/temporary-connection/TemporaryConnectionRpcRemote.js.map +0 -1
  146. package/dist/src/control-layer/ControlLayerNode.d.ts +0 -16
  147. package/dist/src/control-layer/ControlLayerNode.js +0 -3
  148. package/dist/src/control-layer/ControlLayerNode.js.map +0 -1
  149. package/dist/src/control-layer/ExternalNetworkRpc.d.ts +0 -16
  150. package/dist/src/control-layer/ExternalNetworkRpc.js +0 -23
  151. package/dist/src/control-layer/ExternalNetworkRpc.js.map +0 -1
  152. package/dist/src/control-layer/PeerDescriptorStoreManager.d.ts +0 -28
  153. package/dist/src/control-layer/PeerDescriptorStoreManager.js +0 -78
  154. package/dist/src/control-layer/PeerDescriptorStoreManager.js.map +0 -1
  155. package/dist/src/discovery-layer/DiscoveryLayerNode.d.ts +0 -28
  156. package/dist/src/discovery-layer/DiscoveryLayerNode.js +0 -3
  157. package/dist/src/discovery-layer/DiscoveryLayerNode.js.map +0 -1
  158. package/dist/src/exports.d.ts +0 -6
  159. package/dist/src/exports.js +0 -24
  160. package/dist/src/exports.js.map +0 -1
  161. package/dist/src/types.d.ts +0 -6
  162. package/dist/src/types.js +0 -3
  163. package/dist/src/types.js.map +0 -1
  164. package/dist/src/utils.d.ts +0 -3
  165. package/dist/src/utils.js +0 -17
  166. package/dist/src/utils.js.map +0 -1
  167. package/dist/test/benchmark/first-message.d.ts +0 -1
  168. package/dist/test/benchmark/first-message.js +0 -139
  169. package/dist/test/benchmark/first-message.js.map +0 -1
  170. package/dist/test/utils/utils.d.ts +0 -15
  171. package/dist/test/utils/utils.js +0 -106
  172. package/dist/test/utils/utils.js.map +0 -1
@@ -0,0 +1,4489 @@
1
+ 'use strict';
2
+
3
+ var dht = require('@streamr/dht');
4
+ var protoRpc = require('@streamr/proto-rpc');
5
+ var utils = require('@streamr/utils');
6
+ var pull = require('lodash/pull');
7
+ var eventemitter3 = require('eventemitter3');
8
+ var sampleSize = require('lodash/sampleSize');
9
+ var runtime = require('@protobuf-ts/runtime');
10
+ var runtimeRpc = require('@protobuf-ts/runtime-rpc');
11
+ var uuid = require('uuid');
12
+ var sample = require('lodash/sample');
13
+ var yallist = require('yallist');
14
+
15
+ const SERVICE_ID$1 = 'external-network-service';
16
+ class ExternalNetworkRpc {
17
+ rpcCommunicator;
18
+ constructor(transport) {
19
+ this.rpcCommunicator = new dht.ListeningRpcCommunicator(SERVICE_ID$1, transport);
20
+ }
21
+ registerRpcMethod(request, response, name, fn) {
22
+ this.rpcCommunicator.registerRpcMethod(request, response, name, fn);
23
+ }
24
+ createRpcClient(clientClass) {
25
+ return protoRpc.toProtoRpcClient(new clientClass(this.rpcCommunicator.getRpcClientTransport()));
26
+ }
27
+ destroy() {
28
+ this.rpcCommunicator.destroy();
29
+ }
30
+ }
31
+
32
+ var version = "103.2.0-experiment.0";
33
+
34
+ // @generated message type with reflection information, may provide speed optimized methods
35
+ class Any$Type extends runtime.MessageType {
36
+ constructor() {
37
+ super("google.protobuf.Any", [
38
+ { no: 1, name: "type_url", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
39
+ { no: 2, name: "value", kind: "scalar", T: 12 /*ScalarType.BYTES*/ }
40
+ ]);
41
+ }
42
+ /**
43
+ * Pack the message into a new `Any`.
44
+ *
45
+ * Uses 'type.googleapis.com/full.type.name' as the type URL.
46
+ */
47
+ pack(message, type) {
48
+ return {
49
+ typeUrl: this.typeNameToUrl(type.typeName), value: type.toBinary(message),
50
+ };
51
+ }
52
+ /**
53
+ * Unpack the message from the `Any`.
54
+ */
55
+ unpack(any, type, options) {
56
+ if (!this.contains(any, type))
57
+ throw new Error("Cannot unpack google.protobuf.Any with typeUrl '" + any.typeUrl + "' as " + type.typeName + ".");
58
+ return type.fromBinary(any.value, options);
59
+ }
60
+ /**
61
+ * Does the given `Any` contain a packed message of the given type?
62
+ */
63
+ contains(any, type) {
64
+ if (!any.typeUrl.length)
65
+ return false;
66
+ let wants = typeof type == "string" ? type : type.typeName;
67
+ let has = this.typeUrlToName(any.typeUrl);
68
+ return wants === has;
69
+ }
70
+ /**
71
+ * Convert the message to canonical JSON value.
72
+ *
73
+ * You have to provide the `typeRegistry` option so that the
74
+ * packed message can be converted to JSON.
75
+ *
76
+ * The `typeRegistry` option is also required to read
77
+ * `google.protobuf.Any` from JSON format.
78
+ */
79
+ internalJsonWrite(any, options) {
80
+ if (any.typeUrl === "")
81
+ return {};
82
+ let typeName = this.typeUrlToName(any.typeUrl);
83
+ let opt = runtime.jsonWriteOptions(options);
84
+ let type = opt.typeRegistry?.find(t => t.typeName === typeName);
85
+ if (!type)
86
+ throw new globalThis.Error("Unable to convert google.protobuf.Any with typeUrl '" + any.typeUrl + "' to JSON. The specified type " + typeName + " is not available in the type registry.");
87
+ let value = type.fromBinary(any.value, { readUnknownField: false });
88
+ let json = type.internalJsonWrite(value, opt);
89
+ if (typeName.startsWith("google.protobuf.") || !runtime.isJsonObject(json))
90
+ json = { value: json };
91
+ json["@type"] = any.typeUrl;
92
+ return json;
93
+ }
94
+ internalJsonRead(json, options, target) {
95
+ if (!runtime.isJsonObject(json))
96
+ throw new globalThis.Error("Unable to parse google.protobuf.Any from JSON " + runtime.typeofJsonValue(json) + ".");
97
+ if (typeof json["@type"] != "string" || json["@type"] == "")
98
+ return this.create();
99
+ let typeName = this.typeUrlToName(json["@type"]);
100
+ let type = options?.typeRegistry?.find(t => t.typeName == typeName);
101
+ if (!type)
102
+ throw new globalThis.Error("Unable to parse google.protobuf.Any from JSON. The specified type " + typeName + " is not available in the type registry.");
103
+ let value;
104
+ if (typeName.startsWith("google.protobuf.") && json.hasOwnProperty("value"))
105
+ value = type.fromJson(json["value"], options);
106
+ else {
107
+ let copy = Object.assign({}, json);
108
+ delete copy["@type"];
109
+ value = type.fromJson(copy, options);
110
+ }
111
+ if (target === undefined)
112
+ target = this.create();
113
+ target.typeUrl = json["@type"];
114
+ target.value = type.toBinary(value);
115
+ return target;
116
+ }
117
+ typeNameToUrl(name) {
118
+ if (!name.length)
119
+ throw new Error("invalid type name: " + name);
120
+ return "type.googleapis.com/" + name;
121
+ }
122
+ typeUrlToName(url) {
123
+ if (!url.length)
124
+ throw new Error("invalid type url: " + url);
125
+ let slash = url.lastIndexOf("/");
126
+ let name = slash > 0 ? url.substring(slash + 1) : url;
127
+ if (!name.length)
128
+ throw new Error("invalid type url: " + url);
129
+ return name;
130
+ }
131
+ create(value) {
132
+ const message = globalThis.Object.create((this.messagePrototype));
133
+ message.typeUrl = "";
134
+ message.value = new Uint8Array(0);
135
+ if (value !== undefined)
136
+ runtime.reflectionMergePartial(this, message, value);
137
+ return message;
138
+ }
139
+ internalBinaryRead(reader, length, options, target) {
140
+ let message = target ?? this.create(), end = reader.pos + length;
141
+ while (reader.pos < end) {
142
+ let [fieldNo, wireType] = reader.tag();
143
+ switch (fieldNo) {
144
+ case /* string type_url */ 1:
145
+ message.typeUrl = reader.string();
146
+ break;
147
+ case /* bytes value */ 2:
148
+ message.value = reader.bytes();
149
+ break;
150
+ default:
151
+ let u = options.readUnknownField;
152
+ if (u === "throw")
153
+ throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
154
+ let d = reader.skip(wireType);
155
+ if (u !== false)
156
+ (u === true ? runtime.UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
157
+ }
158
+ }
159
+ return message;
160
+ }
161
+ internalBinaryWrite(message, writer, options) {
162
+ /* string type_url = 1; */
163
+ if (message.typeUrl !== "")
164
+ writer.tag(1, runtime.WireType.LengthDelimited).string(message.typeUrl);
165
+ /* bytes value = 2; */
166
+ if (message.value.length)
167
+ writer.tag(2, runtime.WireType.LengthDelimited).bytes(message.value);
168
+ let u = options.writeUnknownFields;
169
+ if (u !== false)
170
+ (u == true ? runtime.UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
171
+ return writer;
172
+ }
173
+ }
174
+ /**
175
+ * @generated MessageType for protobuf message google.protobuf.Any
176
+ */
177
+ const Any = new Any$Type();
178
+
179
+ const parsePeerDescriptor = (dataEntries) => {
180
+ return dataEntries.filter((entry) => !entry.deleted).map((entry) => Any.unpack(entry.data, dht.PeerDescriptor));
181
+ };
182
+ const logger$h = new utils.Logger('PeerDescriptorStoreManager');
183
+ const MAX_NODE_COUNT = 8;
184
+ /**
185
+ * For each key there is usually 0-MAX_NODE_COUNT PeerDescriptors stored in the DHT. If there are fewer node,
186
+ * the peer descriptor of the local node is stored to the DHT.
187
+ */
188
+ class PeerDescriptorStoreManager {
189
+ abortController;
190
+ options;
191
+ isLocalNodeStored_ = false;
192
+ constructor(options) {
193
+ this.options = options;
194
+ this.abortController = new AbortController();
195
+ }
196
+ async fetchNodes() {
197
+ logger$h.trace('Fetch data', { key: this.options.key });
198
+ try {
199
+ const result = await this.options.fetchDataFromDht(this.options.key);
200
+ return parsePeerDescriptor(result);
201
+ }
202
+ catch {
203
+ return [];
204
+ }
205
+ }
206
+ async storeAndKeepLocalNode() {
207
+ if (this.abortController.signal.aborted) {
208
+ return;
209
+ }
210
+ // eslint-disable-next-line no-underscore-dangle
211
+ this.isLocalNodeStored_ = true;
212
+ await this.storeLocalNode();
213
+ await this.keepLocalNode();
214
+ }
215
+ async storeLocalNode() {
216
+ const localPeerDescriptor = this.options.localPeerDescriptor;
217
+ const dataToStore = Any.pack(localPeerDescriptor, dht.PeerDescriptor);
218
+ try {
219
+ await this.options.storeDataToDht(this.options.key, dataToStore);
220
+ }
221
+ catch {
222
+ logger$h.warn('Failed to store local node', { key: this.options.key });
223
+ }
224
+ }
225
+ async keepLocalNode() {
226
+ await utils.scheduleAtInterval(async () => {
227
+ logger$h.trace('Attempting to keep local node', { key: this.options.key });
228
+ try {
229
+ const discovered = await this.fetchNodes();
230
+ if (discovered.length < MAX_NODE_COUNT
231
+ || discovered.some((peerDescriptor) => dht.areEqualPeerDescriptors(peerDescriptor, this.options.localPeerDescriptor))) {
232
+ await this.storeLocalNode();
233
+ }
234
+ }
235
+ catch {
236
+ logger$h.debug('Failed to keep local node', { key: this.options.key });
237
+ }
238
+ }, this.options.storeInterval ?? 60000, false, this.abortController.signal);
239
+ }
240
+ isLocalNodeStored() {
241
+ // eslint-disable-next-line no-underscore-dangle
242
+ return this.isLocalNodeStored_;
243
+ }
244
+ async destroy() {
245
+ this.abortController.abort();
246
+ await this.options.deleteDataFromDht(this.options.key, false);
247
+ }
248
+ }
249
+
250
+ /*
251
+ * Tries to find new neighbors if we currently have less than MIN_NEIGHBOR_COUNT neigbors. It does so by
252
+ * rejoining the stream's control layer network.
253
+ *
254
+ * This way we can avoid some network split scenarios. The functionality is most relevant for small stream
255
+ * networks.
256
+ */
257
+ const logger$g = new utils.Logger('StreamPartNetworkSplitAvoidance');
258
+ const exponentialRunOff = async (task, description, abortSignal, baseDelay = 500, maxAttempts = 6) => {
259
+ for (let i = 1; i <= maxAttempts; i++) {
260
+ if (abortSignal.aborted) {
261
+ return;
262
+ }
263
+ const factor = 2 ** i;
264
+ const delay = baseDelay * factor;
265
+ try {
266
+ await task();
267
+ }
268
+ catch {
269
+ logger$g.trace(`${description} failed, retrying in ${delay} ms`);
270
+ }
271
+ try { // Abort controller throws unexpected errors in destroy?
272
+ await utils.wait(delay, abortSignal);
273
+ }
274
+ catch (err) {
275
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
276
+ logger$g.trace(`${err}`); // TODO Do we need logging?
277
+ }
278
+ }
279
+ };
280
+ const MIN_NEIGHBOR_COUNT = 4;
281
+ class StreamPartNetworkSplitAvoidance {
282
+ abortController;
283
+ options;
284
+ excludedNodes = new Set();
285
+ running = false;
286
+ constructor(options) {
287
+ this.options = options;
288
+ this.abortController = new AbortController();
289
+ }
290
+ async avoidNetworkSplit() {
291
+ this.running = true;
292
+ await exponentialRunOff(async () => {
293
+ const discoveredEntrypoints = await this.options.discoverEntryPoints();
294
+ const filteredEntryPoints = discoveredEntrypoints.filter((peer) => !this.excludedNodes.has(dht.toNodeId(peer)));
295
+ await this.options.discoveryLayerNode.joinDht(filteredEntryPoints, false, false);
296
+ if (this.options.discoveryLayerNode.getNeighborCount() < MIN_NEIGHBOR_COUNT) {
297
+ // Filter out nodes that are not neighbors as those nodes are assumed to be offline
298
+ const newExcludes = filteredEntryPoints
299
+ .filter((peer) => !this.options.discoveryLayerNode.getNeighbors()
300
+ .some((neighbor) => dht.areEqualPeerDescriptors(neighbor, peer)))
301
+ .map((peer) => dht.toNodeId(peer));
302
+ newExcludes.forEach((node) => this.excludedNodes.add(node));
303
+ throw new Error(`Network split is still possible`);
304
+ }
305
+ }, 'avoid network split', this.abortController.signal, this.options.exponentialRunOfBaseDelay);
306
+ this.running = false;
307
+ this.excludedNodes.clear();
308
+ logger$g.trace(`Network split avoided`);
309
+ }
310
+ isRunning() {
311
+ return this.running;
312
+ }
313
+ destroy() {
314
+ this.abortController.abort();
315
+ }
316
+ }
317
+
318
+ const DEFAULT_RECONNECT_INTERVAL = 30 * 1000;
319
+ class StreamPartReconnect {
320
+ abortController;
321
+ discoveryLayerNode;
322
+ peerDescriptorStoreManager;
323
+ constructor(discoveryLayerNode, peerDescriptorStoreManager) {
324
+ this.discoveryLayerNode = discoveryLayerNode;
325
+ this.peerDescriptorStoreManager = peerDescriptorStoreManager;
326
+ }
327
+ async reconnect(timeout = DEFAULT_RECONNECT_INTERVAL) {
328
+ this.abortController = new AbortController();
329
+ await utils.scheduleAtInterval(async () => {
330
+ const entryPoints = await this.peerDescriptorStoreManager.fetchNodes();
331
+ await this.discoveryLayerNode.joinDht(entryPoints);
332
+ // Is is necessary to store the node as an entry point here?
333
+ if (!this.peerDescriptorStoreManager.isLocalNodeStored() && entryPoints.length < MAX_NODE_COUNT) {
334
+ await this.peerDescriptorStoreManager.storeAndKeepLocalNode();
335
+ }
336
+ if (this.discoveryLayerNode.getNeighborCount() > 0) {
337
+ this.abortController.abort();
338
+ }
339
+ }, timeout, true, this.abortController.signal);
340
+ }
341
+ isRunning() {
342
+ return this.abortController ? !this.abortController.signal.aborted : false;
343
+ }
344
+ destroy() {
345
+ this.abortController?.abort();
346
+ }
347
+ }
348
+
349
+ // @generated message type with reflection information, may provide speed optimized methods
350
+ class Empty$Type extends runtime.MessageType {
351
+ constructor() {
352
+ super("google.protobuf.Empty", []);
353
+ }
354
+ create(value) {
355
+ const message = globalThis.Object.create((this.messagePrototype));
356
+ if (value !== undefined)
357
+ runtime.reflectionMergePartial(this, message, value);
358
+ return message;
359
+ }
360
+ internalBinaryRead(reader, length, options, target) {
361
+ let message = target ?? this.create(), end = reader.pos + length;
362
+ while (reader.pos < end) {
363
+ let [fieldNo, wireType] = reader.tag();
364
+ switch (fieldNo) {
365
+ default:
366
+ let u = options.readUnknownField;
367
+ if (u === "throw")
368
+ throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
369
+ let d = reader.skip(wireType);
370
+ if (u !== false)
371
+ (u === true ? runtime.UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
372
+ }
373
+ }
374
+ return message;
375
+ }
376
+ internalBinaryWrite(message, writer, options) {
377
+ let u = options.writeUnknownFields;
378
+ if (u !== false)
379
+ (u == true ? runtime.UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
380
+ return writer;
381
+ }
382
+ }
383
+ /**
384
+ * @generated MessageType for protobuf message google.protobuf.Empty
385
+ */
386
+ const Empty = new Empty$Type();
387
+
388
+ // @generated by protobuf-ts 2.11.1 with parameter server_generic,generate_dependencies,long_type_number
389
+ // @generated from protobuf file "packages/proto-rpc/protos/ProtoRpc.proto" (package "protorpc", syntax proto3)
390
+ // tslint:disable
391
+ /**
392
+ * @generated from protobuf enum protorpc.RpcErrorType
393
+ */
394
+ var RpcErrorType;
395
+ (function (RpcErrorType) {
396
+ /**
397
+ * @generated from protobuf enum value: SERVER_TIMEOUT = 0;
398
+ */
399
+ RpcErrorType[RpcErrorType["SERVER_TIMEOUT"] = 0] = "SERVER_TIMEOUT";
400
+ /**
401
+ * @generated from protobuf enum value: CLIENT_TIMEOUT = 1;
402
+ */
403
+ RpcErrorType[RpcErrorType["CLIENT_TIMEOUT"] = 1] = "CLIENT_TIMEOUT";
404
+ /**
405
+ * @generated from protobuf enum value: UNKNOWN_RPC_METHOD = 2;
406
+ */
407
+ RpcErrorType[RpcErrorType["UNKNOWN_RPC_METHOD"] = 2] = "UNKNOWN_RPC_METHOD";
408
+ /**
409
+ * @generated from protobuf enum value: CLIENT_ERROR = 3;
410
+ */
411
+ RpcErrorType[RpcErrorType["CLIENT_ERROR"] = 3] = "CLIENT_ERROR";
412
+ /**
413
+ * @generated from protobuf enum value: SERVER_ERROR = 4;
414
+ */
415
+ RpcErrorType[RpcErrorType["SERVER_ERROR"] = 4] = "SERVER_ERROR";
416
+ })(RpcErrorType || (RpcErrorType = {}));
417
+ // @generated message type with reflection information, may provide speed optimized methods
418
+ class RpcMessage$Type extends runtime.MessageType {
419
+ constructor() {
420
+ super("protorpc.RpcMessage", [
421
+ { no: 1, name: "header", kind: "map", K: 9 /*ScalarType.STRING*/, V: { kind: "scalar", T: 9 /*ScalarType.STRING*/ } },
422
+ { no: 2, name: "body", kind: "message", T: () => Any },
423
+ { no: 3, name: "requestId", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
424
+ { no: 4, name: "errorType", kind: "enum", opt: true, T: () => ["protorpc.RpcErrorType", RpcErrorType] },
425
+ { no: 5, name: "errorClassName", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ },
426
+ { no: 6, name: "errorCode", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ },
427
+ { no: 7, name: "errorMessage", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ }
428
+ ]);
429
+ }
430
+ }
431
+ /**
432
+ * @generated MessageType for protobuf message protorpc.RpcMessage
433
+ */
434
+ const RpcMessage = new RpcMessage$Type();
435
+ // @generated message type with reflection information, may provide speed optimized methods
436
+ class Mnfo2uhnf92hvqi2nviouq2hv9puhq$Type extends runtime.MessageType {
437
+ constructor() {
438
+ super("protorpc.Mnfo2uhnf92hvqi2nviouq2hv9puhq", [
439
+ { no: 1, name: "empty", kind: "message", T: () => Empty }
440
+ ]);
441
+ }
442
+ }
443
+ /**
444
+ * @generated MessageType for protobuf message protorpc.Mnfo2uhnf92hvqi2nviouq2hv9puhq
445
+ */
446
+ new Mnfo2uhnf92hvqi2nviouq2hv9puhq$Type();
447
+
448
+ // @generated message type with reflection information, may provide speed optimized methods
449
+ class Timestamp$Type extends runtime.MessageType {
450
+ constructor() {
451
+ super("google.protobuf.Timestamp", [
452
+ { no: 1, name: "seconds", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 2 /*LongType.NUMBER*/ },
453
+ { no: 2, name: "nanos", kind: "scalar", T: 5 /*ScalarType.INT32*/ }
454
+ ]);
455
+ }
456
+ /**
457
+ * Creates a new `Timestamp` for the current time.
458
+ */
459
+ now() {
460
+ const msg = this.create();
461
+ const ms = Date.now();
462
+ msg.seconds = runtime.PbLong.from(Math.floor(ms / 1000)).toNumber();
463
+ msg.nanos = (ms % 1000) * 1000000;
464
+ return msg;
465
+ }
466
+ /**
467
+ * Converts a `Timestamp` to a JavaScript Date.
468
+ */
469
+ toDate(message) {
470
+ return new Date(runtime.PbLong.from(message.seconds).toNumber() * 1000 + Math.ceil(message.nanos / 1000000));
471
+ }
472
+ /**
473
+ * Converts a JavaScript Date to a `Timestamp`.
474
+ */
475
+ fromDate(date) {
476
+ const msg = this.create();
477
+ const ms = date.getTime();
478
+ msg.seconds = runtime.PbLong.from(Math.floor(ms / 1000)).toNumber();
479
+ msg.nanos = ((ms % 1000) + (ms < 0 && ms % 1000 !== 0 ? 1000 : 0)) * 1000000;
480
+ return msg;
481
+ }
482
+ /**
483
+ * In JSON format, the `Timestamp` type is encoded as a string
484
+ * in the RFC 3339 format.
485
+ */
486
+ internalJsonWrite(message, options) {
487
+ let ms = runtime.PbLong.from(message.seconds).toNumber() * 1000;
488
+ if (ms < Date.parse("0001-01-01T00:00:00Z") || ms > Date.parse("9999-12-31T23:59:59Z"))
489
+ throw new Error("Unable to encode Timestamp to JSON. Must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive.");
490
+ if (message.nanos < 0)
491
+ throw new Error("Unable to encode invalid Timestamp to JSON. Nanos must not be negative.");
492
+ let z = "Z";
493
+ if (message.nanos > 0) {
494
+ let nanosStr = (message.nanos + 1000000000).toString().substring(1);
495
+ if (nanosStr.substring(3) === "000000")
496
+ z = "." + nanosStr.substring(0, 3) + "Z";
497
+ else if (nanosStr.substring(6) === "000")
498
+ z = "." + nanosStr.substring(0, 6) + "Z";
499
+ else
500
+ z = "." + nanosStr + "Z";
501
+ }
502
+ return new Date(ms).toISOString().replace(".000Z", z);
503
+ }
504
+ /**
505
+ * In JSON format, the `Timestamp` type is encoded as a string
506
+ * in the RFC 3339 format.
507
+ */
508
+ internalJsonRead(json, options, target) {
509
+ if (typeof json !== "string")
510
+ throw new Error("Unable to parse Timestamp from JSON " + runtime.typeofJsonValue(json) + ".");
511
+ let matches = json.match(/^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(?:Z|\.([0-9]{3,9})Z|([+-][0-9][0-9]:[0-9][0-9]))$/);
512
+ if (!matches)
513
+ throw new Error("Unable to parse Timestamp from JSON. Invalid format.");
514
+ let ms = Date.parse(matches[1] + "-" + matches[2] + "-" + matches[3] + "T" + matches[4] + ":" + matches[5] + ":" + matches[6] + (matches[8] ? matches[8] : "Z"));
515
+ if (Number.isNaN(ms))
516
+ throw new Error("Unable to parse Timestamp from JSON. Invalid value.");
517
+ if (ms < Date.parse("0001-01-01T00:00:00Z") || ms > Date.parse("9999-12-31T23:59:59Z"))
518
+ throw new globalThis.Error("Unable to parse Timestamp from JSON. Must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive.");
519
+ if (!target)
520
+ target = this.create();
521
+ target.seconds = runtime.PbLong.from(ms / 1000).toNumber();
522
+ target.nanos = 0;
523
+ if (matches[7])
524
+ target.nanos = (parseInt("1" + matches[7] + "0".repeat(9 - matches[7].length)) - 1000000000);
525
+ return target;
526
+ }
527
+ create(value) {
528
+ const message = globalThis.Object.create((this.messagePrototype));
529
+ message.seconds = 0;
530
+ message.nanos = 0;
531
+ if (value !== undefined)
532
+ runtime.reflectionMergePartial(this, message, value);
533
+ return message;
534
+ }
535
+ internalBinaryRead(reader, length, options, target) {
536
+ let message = target ?? this.create(), end = reader.pos + length;
537
+ while (reader.pos < end) {
538
+ let [fieldNo, wireType] = reader.tag();
539
+ switch (fieldNo) {
540
+ case /* int64 seconds */ 1:
541
+ message.seconds = reader.int64().toNumber();
542
+ break;
543
+ case /* int32 nanos */ 2:
544
+ message.nanos = reader.int32();
545
+ break;
546
+ default:
547
+ let u = options.readUnknownField;
548
+ if (u === "throw")
549
+ throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
550
+ let d = reader.skip(wireType);
551
+ if (u !== false)
552
+ (u === true ? runtime.UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
553
+ }
554
+ }
555
+ return message;
556
+ }
557
+ internalBinaryWrite(message, writer, options) {
558
+ /* int64 seconds = 1; */
559
+ if (message.seconds !== 0)
560
+ writer.tag(1, runtime.WireType.Varint).int64(message.seconds);
561
+ /* int32 nanos = 2; */
562
+ if (message.nanos !== 0)
563
+ writer.tag(2, runtime.WireType.Varint).int32(message.nanos);
564
+ let u = options.writeUnknownFields;
565
+ if (u !== false)
566
+ (u == true ? runtime.UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
567
+ return writer;
568
+ }
569
+ }
570
+ /**
571
+ * @generated MessageType for protobuf message google.protobuf.Timestamp
572
+ */
573
+ const Timestamp = new Timestamp$Type();
574
+
575
+ // @generated by protobuf-ts 2.11.1 with parameter server_generic,generate_dependencies,long_type_number
576
+ // @generated from protobuf file "packages/dht/protos/DhtRpc.proto" (package "dht", syntax proto3)
577
+ // tslint:disable
578
+ /**
579
+ * @generated from protobuf enum dht.RecursiveOperation
580
+ */
581
+ var RecursiveOperation;
582
+ (function (RecursiveOperation) {
583
+ /**
584
+ * @generated from protobuf enum value: FIND_CLOSEST_NODES = 0;
585
+ */
586
+ RecursiveOperation[RecursiveOperation["FIND_CLOSEST_NODES"] = 0] = "FIND_CLOSEST_NODES";
587
+ /**
588
+ * @generated from protobuf enum value: FETCH_DATA = 1;
589
+ */
590
+ RecursiveOperation[RecursiveOperation["FETCH_DATA"] = 1] = "FETCH_DATA";
591
+ /**
592
+ * @generated from protobuf enum value: DELETE_DATA = 2;
593
+ */
594
+ RecursiveOperation[RecursiveOperation["DELETE_DATA"] = 2] = "DELETE_DATA";
595
+ })(RecursiveOperation || (RecursiveOperation = {}));
596
+ /**
597
+ * @generated from protobuf enum dht.NodeType
598
+ */
599
+ var NodeType;
600
+ (function (NodeType) {
601
+ /**
602
+ * @generated from protobuf enum value: NODEJS = 0;
603
+ */
604
+ NodeType[NodeType["NODEJS"] = 0] = "NODEJS";
605
+ /**
606
+ * @generated from protobuf enum value: BROWSER = 1;
607
+ */
608
+ NodeType[NodeType["BROWSER"] = 1] = "BROWSER";
609
+ })(NodeType || (NodeType = {}));
610
+ /**
611
+ * @generated from protobuf enum dht.RpcResponseError
612
+ */
613
+ var RpcResponseError;
614
+ (function (RpcResponseError) {
615
+ /**
616
+ * @generated from protobuf enum value: SERVER_TIMOUT = 0;
617
+ */
618
+ RpcResponseError[RpcResponseError["SERVER_TIMOUT"] = 0] = "SERVER_TIMOUT";
619
+ /**
620
+ * @generated from protobuf enum value: CLIENT_TIMEOUT = 1;
621
+ */
622
+ RpcResponseError[RpcResponseError["CLIENT_TIMEOUT"] = 1] = "CLIENT_TIMEOUT";
623
+ /**
624
+ * @generated from protobuf enum value: SERVER_ERROR = 2;
625
+ */
626
+ RpcResponseError[RpcResponseError["SERVER_ERROR"] = 2] = "SERVER_ERROR";
627
+ /**
628
+ * @generated from protobuf enum value: UNKNOWN_RPC_METHOD = 3;
629
+ */
630
+ RpcResponseError[RpcResponseError["UNKNOWN_RPC_METHOD"] = 3] = "UNKNOWN_RPC_METHOD";
631
+ })(RpcResponseError || (RpcResponseError = {}));
632
+ /**
633
+ * @generated from protobuf enum dht.RouteMessageError
634
+ */
635
+ var RouteMessageError;
636
+ (function (RouteMessageError) {
637
+ /**
638
+ * @generated from protobuf enum value: NO_TARGETS = 0;
639
+ */
640
+ RouteMessageError[RouteMessageError["NO_TARGETS"] = 0] = "NO_TARGETS";
641
+ /**
642
+ * @generated from protobuf enum value: DUPLICATE = 1;
643
+ */
644
+ RouteMessageError[RouteMessageError["DUPLICATE"] = 1] = "DUPLICATE";
645
+ /**
646
+ * TODO: can this be removed? If DhtNode is already stopped the server side requests
647
+ * should not be processed
648
+ *
649
+ * @generated from protobuf enum value: STOPPED = 2;
650
+ */
651
+ RouteMessageError[RouteMessageError["STOPPED"] = 2] = "STOPPED";
652
+ })(RouteMessageError || (RouteMessageError = {}));
653
+ /**
654
+ * @generated from protobuf enum dht.HandshakeError
655
+ */
656
+ var HandshakeError;
657
+ (function (HandshakeError) {
658
+ /**
659
+ * @generated from protobuf enum value: DUPLICATE_CONNECTION = 0;
660
+ */
661
+ HandshakeError[HandshakeError["DUPLICATE_CONNECTION"] = 0] = "DUPLICATE_CONNECTION";
662
+ /**
663
+ * @generated from protobuf enum value: INVALID_TARGET_PEER_DESCRIPTOR = 1;
664
+ */
665
+ HandshakeError[HandshakeError["INVALID_TARGET_PEER_DESCRIPTOR"] = 1] = "INVALID_TARGET_PEER_DESCRIPTOR";
666
+ /**
667
+ * @generated from protobuf enum value: UNSUPPORTED_PROTOCOL_VERSION = 2;
668
+ */
669
+ HandshakeError[HandshakeError["UNSUPPORTED_PROTOCOL_VERSION"] = 2] = "UNSUPPORTED_PROTOCOL_VERSION";
670
+ })(HandshakeError || (HandshakeError = {}));
671
+ /**
672
+ * @generated from protobuf enum dht.DisconnectMode
673
+ */
674
+ var DisconnectMode;
675
+ (function (DisconnectMode) {
676
+ /**
677
+ * @generated from protobuf enum value: NORMAL = 0;
678
+ */
679
+ DisconnectMode[DisconnectMode["NORMAL"] = 0] = "NORMAL";
680
+ /**
681
+ * @generated from protobuf enum value: LEAVING = 1;
682
+ */
683
+ DisconnectMode[DisconnectMode["LEAVING"] = 1] = "LEAVING";
684
+ })(DisconnectMode || (DisconnectMode = {}));
685
+ // @generated message type with reflection information, may provide speed optimized methods
686
+ class StoreDataRequest$Type extends runtime.MessageType {
687
+ constructor() {
688
+ super("dht.StoreDataRequest", [
689
+ { no: 1, name: "key", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
690
+ { no: 2, name: "data", kind: "message", T: () => Any },
691
+ { no: 3, name: "creator", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
692
+ { no: 4, name: "createdAt", kind: "message", T: () => Timestamp },
693
+ { no: 5, name: "ttl", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }
694
+ ]);
695
+ }
696
+ }
697
+ /**
698
+ * @generated MessageType for protobuf message dht.StoreDataRequest
699
+ */
700
+ const StoreDataRequest = new StoreDataRequest$Type();
701
+ // @generated message type with reflection information, may provide speed optimized methods
702
+ class StoreDataResponse$Type extends runtime.MessageType {
703
+ constructor() {
704
+ super("dht.StoreDataResponse", []);
705
+ }
706
+ }
707
+ /**
708
+ * @generated MessageType for protobuf message dht.StoreDataResponse
709
+ */
710
+ const StoreDataResponse = new StoreDataResponse$Type();
711
+ // @generated message type with reflection information, may provide speed optimized methods
712
+ class ExternalStoreDataRequest$Type extends runtime.MessageType {
713
+ constructor() {
714
+ super("dht.ExternalStoreDataRequest", [
715
+ { no: 1, name: "key", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
716
+ { no: 2, name: "data", kind: "message", T: () => Any }
717
+ ]);
718
+ }
719
+ }
720
+ /**
721
+ * @generated MessageType for protobuf message dht.ExternalStoreDataRequest
722
+ */
723
+ const ExternalStoreDataRequest = new ExternalStoreDataRequest$Type();
724
+ // @generated message type with reflection information, may provide speed optimized methods
725
+ class ExternalStoreDataResponse$Type extends runtime.MessageType {
726
+ constructor() {
727
+ super("dht.ExternalStoreDataResponse", [
728
+ { no: 1, name: "storers", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => PeerDescriptor }
729
+ ]);
730
+ }
731
+ }
732
+ /**
733
+ * @generated MessageType for protobuf message dht.ExternalStoreDataResponse
734
+ */
735
+ const ExternalStoreDataResponse = new ExternalStoreDataResponse$Type();
736
+ // @generated message type with reflection information, may provide speed optimized methods
737
+ class ReplicateDataRequest$Type extends runtime.MessageType {
738
+ constructor() {
739
+ super("dht.ReplicateDataRequest", [
740
+ { no: 1, name: "entry", kind: "message", T: () => DataEntry }
741
+ ]);
742
+ }
743
+ }
744
+ /**
745
+ * @generated MessageType for protobuf message dht.ReplicateDataRequest
746
+ */
747
+ const ReplicateDataRequest = new ReplicateDataRequest$Type();
748
+ // @generated message type with reflection information, may provide speed optimized methods
749
+ class DataEntry$Type extends runtime.MessageType {
750
+ constructor() {
751
+ super("dht.DataEntry", [
752
+ { no: 1, name: "key", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
753
+ { no: 2, name: "data", kind: "message", T: () => Any },
754
+ { no: 3, name: "creator", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
755
+ { no: 4, name: "createdAt", kind: "message", T: () => Timestamp },
756
+ { no: 5, name: "storedAt", kind: "message", T: () => Timestamp },
757
+ { no: 6, name: "ttl", kind: "scalar", T: 13 /*ScalarType.UINT32*/ },
758
+ { no: 7, name: "stale", kind: "scalar", T: 8 /*ScalarType.BOOL*/ },
759
+ { no: 8, name: "deleted", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }
760
+ ]);
761
+ }
762
+ }
763
+ /**
764
+ * @generated MessageType for protobuf message dht.DataEntry
765
+ */
766
+ const DataEntry = new DataEntry$Type();
767
+ // @generated message type with reflection information, may provide speed optimized methods
768
+ class ClosestPeersRequest$Type extends runtime.MessageType {
769
+ constructor() {
770
+ super("dht.ClosestPeersRequest", [
771
+ { no: 1, name: "nodeId", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
772
+ { no: 2, name: "requestId", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
773
+ ]);
774
+ }
775
+ }
776
+ /**
777
+ * @generated MessageType for protobuf message dht.ClosestPeersRequest
778
+ */
779
+ const ClosestPeersRequest = new ClosestPeersRequest$Type();
780
+ // @generated message type with reflection information, may provide speed optimized methods
781
+ class ClosestPeersResponse$Type extends runtime.MessageType {
782
+ constructor() {
783
+ super("dht.ClosestPeersResponse", [
784
+ { no: 1, name: "peers", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => PeerDescriptor },
785
+ { no: 2, name: "requestId", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
786
+ ]);
787
+ }
788
+ }
789
+ /**
790
+ * @generated MessageType for protobuf message dht.ClosestPeersResponse
791
+ */
792
+ const ClosestPeersResponse = new ClosestPeersResponse$Type();
793
+ // @generated message type with reflection information, may provide speed optimized methods
794
+ class ClosestRingPeersRequest$Type extends runtime.MessageType {
795
+ constructor() {
796
+ super("dht.ClosestRingPeersRequest", [
797
+ { no: 1, name: "ringId", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
798
+ { no: 2, name: "requestId", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
799
+ ]);
800
+ }
801
+ }
802
+ /**
803
+ * @generated MessageType for protobuf message dht.ClosestRingPeersRequest
804
+ */
805
+ const ClosestRingPeersRequest = new ClosestRingPeersRequest$Type();
806
+ // @generated message type with reflection information, may provide speed optimized methods
807
+ class ClosestRingPeersResponse$Type extends runtime.MessageType {
808
+ constructor() {
809
+ super("dht.ClosestRingPeersResponse", [
810
+ { no: 1, name: "leftPeers", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => PeerDescriptor },
811
+ { no: 2, name: "rightPeers", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => PeerDescriptor },
812
+ { no: 3, name: "requestId", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
813
+ ]);
814
+ }
815
+ }
816
+ /**
817
+ * @generated MessageType for protobuf message dht.ClosestRingPeersResponse
818
+ */
819
+ const ClosestRingPeersResponse = new ClosestRingPeersResponse$Type();
820
+ // @generated message type with reflection information, may provide speed optimized methods
821
+ class RecursiveOperationRequest$Type extends runtime.MessageType {
822
+ constructor() {
823
+ super("dht.RecursiveOperationRequest", [
824
+ { no: 1, name: "sessionId", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
825
+ { no: 2, name: "operation", kind: "enum", T: () => ["dht.RecursiveOperation", RecursiveOperation] }
826
+ ]);
827
+ }
828
+ }
829
+ /**
830
+ * @generated MessageType for protobuf message dht.RecursiveOperationRequest
831
+ */
832
+ const RecursiveOperationRequest = new RecursiveOperationRequest$Type();
833
+ // @generated message type with reflection information, may provide speed optimized methods
834
+ class RecursiveOperationResponse$Type extends runtime.MessageType {
835
+ constructor() {
836
+ super("dht.RecursiveOperationResponse", [
837
+ { no: 1, name: "closestConnectedNodes", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => PeerDescriptor },
838
+ { no: 2, name: "dataEntries", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => DataEntry },
839
+ { no: 3, name: "noCloserNodesFound", kind: "scalar", T: 8 /*ScalarType.BOOL*/ },
840
+ { no: 4, name: "routingPath", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => PeerDescriptor }
841
+ ]);
842
+ }
843
+ }
844
+ /**
845
+ * @generated MessageType for protobuf message dht.RecursiveOperationResponse
846
+ */
847
+ const RecursiveOperationResponse = new RecursiveOperationResponse$Type();
848
+ // @generated message type with reflection information, may provide speed optimized methods
849
+ class PingRequest$Type extends runtime.MessageType {
850
+ constructor() {
851
+ super("dht.PingRequest", [
852
+ { no: 1, name: "requestId", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
853
+ ]);
854
+ }
855
+ }
856
+ /**
857
+ * @generated MessageType for protobuf message dht.PingRequest
858
+ */
859
+ const PingRequest = new PingRequest$Type();
860
+ // @generated message type with reflection information, may provide speed optimized methods
861
+ class PingResponse$Type extends runtime.MessageType {
862
+ constructor() {
863
+ super("dht.PingResponse", [
864
+ { no: 1, name: "requestId", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
865
+ ]);
866
+ }
867
+ }
868
+ /**
869
+ * @generated MessageType for protobuf message dht.PingResponse
870
+ */
871
+ const PingResponse = new PingResponse$Type();
872
+ // @generated message type with reflection information, may provide speed optimized methods
873
+ class LeaveNotice$Type extends runtime.MessageType {
874
+ constructor() {
875
+ super("dht.LeaveNotice", []);
876
+ }
877
+ }
878
+ /**
879
+ * @generated MessageType for protobuf message dht.LeaveNotice
880
+ */
881
+ const LeaveNotice = new LeaveNotice$Type();
882
+ // @generated message type with reflection information, may provide speed optimized methods
883
+ class PeerDescriptor$Type extends runtime.MessageType {
884
+ constructor() {
885
+ super("dht.PeerDescriptor", [
886
+ { no: 1, name: "nodeId", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
887
+ { no: 2, name: "type", kind: "enum", T: () => ["dht.NodeType", NodeType] },
888
+ { no: 3, name: "udp", kind: "message", T: () => ConnectivityMethod },
889
+ { no: 4, name: "tcp", kind: "message", T: () => ConnectivityMethod },
890
+ { no: 5, name: "websocket", kind: "message", T: () => ConnectivityMethod },
891
+ { no: 6, name: "region", kind: "scalar", opt: true, T: 13 /*ScalarType.UINT32*/ },
892
+ { no: 7, name: "ipAddress", kind: "scalar", opt: true, T: 13 /*ScalarType.UINT32*/ },
893
+ { no: 8, name: "publicKey", kind: "scalar", opt: true, T: 12 /*ScalarType.BYTES*/ },
894
+ { no: 9, name: "signature", kind: "scalar", opt: true, T: 12 /*ScalarType.BYTES*/ }
895
+ ]);
896
+ }
897
+ }
898
+ /**
899
+ * @generated MessageType for protobuf message dht.PeerDescriptor
900
+ */
901
+ const PeerDescriptor = new PeerDescriptor$Type();
902
+ // @generated message type with reflection information, may provide speed optimized methods
903
+ class ConnectivityMethod$Type extends runtime.MessageType {
904
+ constructor() {
905
+ super("dht.ConnectivityMethod", [
906
+ { no: 1, name: "port", kind: "scalar", T: 13 /*ScalarType.UINT32*/ },
907
+ { no: 2, name: "host", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
908
+ { no: 3, name: "tls", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }
909
+ ]);
910
+ }
911
+ }
912
+ /**
913
+ * @generated MessageType for protobuf message dht.ConnectivityMethod
914
+ */
915
+ const ConnectivityMethod = new ConnectivityMethod$Type();
916
+ // @generated message type with reflection information, may provide speed optimized methods
917
+ class RouteMessageWrapper$Type extends runtime.MessageType {
918
+ constructor() {
919
+ super("dht.RouteMessageWrapper", [
920
+ { no: 1, name: "requestId", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
921
+ { no: 2, name: "sourcePeer", kind: "message", T: () => PeerDescriptor },
922
+ { no: 3, name: "target", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
923
+ { no: 4, name: "message", kind: "message", T: () => Message },
924
+ { no: 5, name: "reachableThrough", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => PeerDescriptor },
925
+ { no: 6, name: "routingPath", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => PeerDescriptor },
926
+ { no: 7, name: "parallelRootNodeIds", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ }
927
+ ]);
928
+ }
929
+ }
930
+ /**
931
+ * @generated MessageType for protobuf message dht.RouteMessageWrapper
932
+ */
933
+ const RouteMessageWrapper = new RouteMessageWrapper$Type();
934
+ // @generated message type with reflection information, may provide speed optimized methods
935
+ class RouteMessageAck$Type extends runtime.MessageType {
936
+ constructor() {
937
+ super("dht.RouteMessageAck", [
938
+ { no: 1, name: "requestId", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
939
+ { no: 2, name: "error", kind: "enum", opt: true, T: () => ["dht.RouteMessageError", RouteMessageError] }
940
+ ]);
941
+ }
942
+ }
943
+ /**
944
+ * @generated MessageType for protobuf message dht.RouteMessageAck
945
+ */
946
+ const RouteMessageAck = new RouteMessageAck$Type();
947
+ // @generated message type with reflection information, may provide speed optimized methods
948
+ class ConnectivityRequest$Type extends runtime.MessageType {
949
+ constructor() {
950
+ super("dht.ConnectivityRequest", [
951
+ { no: 1, name: "port", kind: "scalar", T: 13 /*ScalarType.UINT32*/ },
952
+ { no: 2, name: "tls", kind: "scalar", T: 8 /*ScalarType.BOOL*/ },
953
+ { no: 3, name: "host", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ },
954
+ { no: 4, name: "allowSelfSignedCertificate", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }
955
+ ]);
956
+ }
957
+ }
958
+ /**
959
+ * @generated MessageType for protobuf message dht.ConnectivityRequest
960
+ */
961
+ const ConnectivityRequest = new ConnectivityRequest$Type();
962
+ // @generated message type with reflection information, may provide speed optimized methods
963
+ class ConnectivityResponse$Type extends runtime.MessageType {
964
+ constructor() {
965
+ super("dht.ConnectivityResponse", [
966
+ { no: 1, name: "host", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
967
+ { no: 2, name: "natType", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
968
+ { no: 3, name: "websocket", kind: "message", T: () => ConnectivityMethod },
969
+ { no: 4, name: "ipAddress", kind: "scalar", T: 13 /*ScalarType.UINT32*/ },
970
+ { no: 5, name: "protocolVersion", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
971
+ { no: 6, name: "latitude", kind: "scalar", opt: true, T: 1 /*ScalarType.DOUBLE*/ },
972
+ { no: 7, name: "longitude", kind: "scalar", opt: true, T: 1 /*ScalarType.DOUBLE*/ }
973
+ ]);
974
+ }
975
+ }
976
+ /**
977
+ * @generated MessageType for protobuf message dht.ConnectivityResponse
978
+ */
979
+ const ConnectivityResponse = new ConnectivityResponse$Type();
980
+ // @generated message type with reflection information, may provide speed optimized methods
981
+ class HandshakeRequest$Type extends runtime.MessageType {
982
+ constructor() {
983
+ super("dht.HandshakeRequest", [
984
+ { no: 1, name: "sourcePeerDescriptor", kind: "message", T: () => PeerDescriptor },
985
+ { no: 2, name: "targetPeerDescriptor", kind: "message", T: () => PeerDescriptor },
986
+ { no: 3, name: "protocolVersion", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
987
+ { no: 4, name: "applicationVersion", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
988
+ ]);
989
+ }
990
+ }
991
+ /**
992
+ * @generated MessageType for protobuf message dht.HandshakeRequest
993
+ */
994
+ const HandshakeRequest = new HandshakeRequest$Type();
995
+ // @generated message type with reflection information, may provide speed optimized methods
996
+ class HandshakeResponse$Type extends runtime.MessageType {
997
+ constructor() {
998
+ super("dht.HandshakeResponse", [
999
+ { no: 1, name: "sourcePeerDescriptor", kind: "message", T: () => PeerDescriptor },
1000
+ { no: 2, name: "error", kind: "enum", opt: true, T: () => ["dht.HandshakeError", HandshakeError] },
1001
+ { no: 3, name: "protocolVersion", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
1002
+ { no: 4, name: "applicationVersion", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
1003
+ ]);
1004
+ }
1005
+ }
1006
+ /**
1007
+ * @generated MessageType for protobuf message dht.HandshakeResponse
1008
+ */
1009
+ const HandshakeResponse = new HandshakeResponse$Type();
1010
+ // @generated message type with reflection information, may provide speed optimized methods
1011
+ class Message$Type extends runtime.MessageType {
1012
+ constructor() {
1013
+ super("dht.Message", [
1014
+ { no: 1, name: "messageId", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
1015
+ { no: 2, name: "sourceDescriptor", kind: "message", T: () => PeerDescriptor },
1016
+ { no: 3, name: "targetDescriptor", kind: "message", T: () => PeerDescriptor },
1017
+ { no: 4, name: "serviceId", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
1018
+ { no: 5, name: "rpcMessage", kind: "message", oneof: "body", T: () => RpcMessage },
1019
+ { no: 6, name: "connectivityRequest", kind: "message", oneof: "body", T: () => ConnectivityRequest },
1020
+ { no: 7, name: "connectivityResponse", kind: "message", oneof: "body", T: () => ConnectivityResponse },
1021
+ { no: 8, name: "handshakeRequest", kind: "message", oneof: "body", T: () => HandshakeRequest },
1022
+ { no: 9, name: "handshakeResponse", kind: "message", oneof: "body", T: () => HandshakeResponse },
1023
+ { no: 10, name: "recursiveOperationRequest", kind: "message", oneof: "body", T: () => RecursiveOperationRequest }
1024
+ ]);
1025
+ }
1026
+ }
1027
+ /**
1028
+ * @generated MessageType for protobuf message dht.Message
1029
+ */
1030
+ const Message = new Message$Type();
1031
+ // @generated message type with reflection information, may provide speed optimized methods
1032
+ class WebsocketConnectionRequest$Type extends runtime.MessageType {
1033
+ constructor() {
1034
+ super("dht.WebsocketConnectionRequest", []);
1035
+ }
1036
+ }
1037
+ /**
1038
+ * @generated MessageType for protobuf message dht.WebsocketConnectionRequest
1039
+ */
1040
+ const WebsocketConnectionRequest = new WebsocketConnectionRequest$Type();
1041
+ // @generated message type with reflection information, may provide speed optimized methods
1042
+ class WebrtcConnectionRequest$Type extends runtime.MessageType {
1043
+ constructor() {
1044
+ super("dht.WebrtcConnectionRequest", []);
1045
+ }
1046
+ }
1047
+ /**
1048
+ * @generated MessageType for protobuf message dht.WebrtcConnectionRequest
1049
+ */
1050
+ const WebrtcConnectionRequest = new WebrtcConnectionRequest$Type();
1051
+ // @generated message type with reflection information, may provide speed optimized methods
1052
+ class RtcOffer$Type extends runtime.MessageType {
1053
+ constructor() {
1054
+ super("dht.RtcOffer", [
1055
+ { no: 1, name: "description", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
1056
+ { no: 2, name: "connectionId", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
1057
+ ]);
1058
+ }
1059
+ }
1060
+ /**
1061
+ * @generated MessageType for protobuf message dht.RtcOffer
1062
+ */
1063
+ const RtcOffer = new RtcOffer$Type();
1064
+ // @generated message type with reflection information, may provide speed optimized methods
1065
+ class RtcAnswer$Type extends runtime.MessageType {
1066
+ constructor() {
1067
+ super("dht.RtcAnswer", [
1068
+ { no: 1, name: "description", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
1069
+ { no: 2, name: "connectionId", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
1070
+ ]);
1071
+ }
1072
+ }
1073
+ /**
1074
+ * @generated MessageType for protobuf message dht.RtcAnswer
1075
+ */
1076
+ const RtcAnswer = new RtcAnswer$Type();
1077
+ // @generated message type with reflection information, may provide speed optimized methods
1078
+ class IceCandidate$Type extends runtime.MessageType {
1079
+ constructor() {
1080
+ super("dht.IceCandidate", [
1081
+ { no: 1, name: "candidate", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
1082
+ { no: 2, name: "mid", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
1083
+ { no: 3, name: "connectionId", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
1084
+ ]);
1085
+ }
1086
+ }
1087
+ /**
1088
+ * @generated MessageType for protobuf message dht.IceCandidate
1089
+ */
1090
+ const IceCandidate = new IceCandidate$Type();
1091
+ // @generated message type with reflection information, may provide speed optimized methods
1092
+ class LockRequest$Type extends runtime.MessageType {
1093
+ constructor() {
1094
+ super("dht.LockRequest", [
1095
+ { no: 1, name: "lockId", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
1096
+ ]);
1097
+ }
1098
+ }
1099
+ /**
1100
+ * @generated MessageType for protobuf message dht.LockRequest
1101
+ */
1102
+ const LockRequest = new LockRequest$Type();
1103
+ // @generated message type with reflection information, may provide speed optimized methods
1104
+ class UnlockRequest$Type extends runtime.MessageType {
1105
+ constructor() {
1106
+ super("dht.UnlockRequest", [
1107
+ { no: 1, name: "lockId", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
1108
+ ]);
1109
+ }
1110
+ }
1111
+ /**
1112
+ * @generated MessageType for protobuf message dht.UnlockRequest
1113
+ */
1114
+ const UnlockRequest = new UnlockRequest$Type();
1115
+ // @generated message type with reflection information, may provide speed optimized methods
1116
+ class LockResponse$Type extends runtime.MessageType {
1117
+ constructor() {
1118
+ super("dht.LockResponse", [
1119
+ { no: 1, name: "accepted", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }
1120
+ ]);
1121
+ }
1122
+ }
1123
+ /**
1124
+ * @generated MessageType for protobuf message dht.LockResponse
1125
+ */
1126
+ const LockResponse = new LockResponse$Type();
1127
+ // @generated message type with reflection information, may provide speed optimized methods
1128
+ class DisconnectNotice$Type extends runtime.MessageType {
1129
+ constructor() {
1130
+ super("dht.DisconnectNotice", [
1131
+ { no: 1, name: "disconnectMode", kind: "enum", T: () => ["dht.DisconnectMode", DisconnectMode] }
1132
+ ]);
1133
+ }
1134
+ }
1135
+ /**
1136
+ * @generated MessageType for protobuf message dht.DisconnectNotice
1137
+ */
1138
+ const DisconnectNotice = new DisconnectNotice$Type();
1139
+ // @generated message type with reflection information, may provide speed optimized methods
1140
+ class SetPrivateRequest$Type extends runtime.MessageType {
1141
+ constructor() {
1142
+ super("dht.SetPrivateRequest", [
1143
+ { no: 1, name: "isPrivate", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }
1144
+ ]);
1145
+ }
1146
+ }
1147
+ /**
1148
+ * @generated MessageType for protobuf message dht.SetPrivateRequest
1149
+ */
1150
+ const SetPrivateRequest = new SetPrivateRequest$Type();
1151
+ // @generated message type with reflection information, may provide speed optimized methods
1152
+ class ExternalFetchDataRequest$Type extends runtime.MessageType {
1153
+ constructor() {
1154
+ super("dht.ExternalFetchDataRequest", [
1155
+ { no: 1, name: "key", kind: "scalar", T: 12 /*ScalarType.BYTES*/ }
1156
+ ]);
1157
+ }
1158
+ }
1159
+ /**
1160
+ * @generated MessageType for protobuf message dht.ExternalFetchDataRequest
1161
+ */
1162
+ const ExternalFetchDataRequest = new ExternalFetchDataRequest$Type();
1163
+ // @generated message type with reflection information, may provide speed optimized methods
1164
+ class ExternalFetchDataResponse$Type extends runtime.MessageType {
1165
+ constructor() {
1166
+ super("dht.ExternalFetchDataResponse", [
1167
+ { no: 1, name: "entries", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => DataEntry }
1168
+ ]);
1169
+ }
1170
+ }
1171
+ /**
1172
+ * @generated MessageType for protobuf message dht.ExternalFetchDataResponse
1173
+ */
1174
+ const ExternalFetchDataResponse = new ExternalFetchDataResponse$Type();
1175
+ // @generated message type with reflection information, may provide speed optimized methods
1176
+ class ExternalFindClosestNodesRequest$Type extends runtime.MessageType {
1177
+ constructor() {
1178
+ super("dht.ExternalFindClosestNodesRequest", [
1179
+ { no: 1, name: "nodeId", kind: "scalar", T: 12 /*ScalarType.BYTES*/ }
1180
+ ]);
1181
+ }
1182
+ }
1183
+ /**
1184
+ * @generated MessageType for protobuf message dht.ExternalFindClosestNodesRequest
1185
+ */
1186
+ const ExternalFindClosestNodesRequest = new ExternalFindClosestNodesRequest$Type();
1187
+ // @generated message type with reflection information, may provide speed optimized methods
1188
+ class ExternalFindClosestNodesResponse$Type extends runtime.MessageType {
1189
+ constructor() {
1190
+ super("dht.ExternalFindClosestNodesResponse", [
1191
+ { no: 1, name: "closestNodes", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => PeerDescriptor }
1192
+ ]);
1193
+ }
1194
+ }
1195
+ /**
1196
+ * @generated MessageType for protobuf message dht.ExternalFindClosestNodesResponse
1197
+ */
1198
+ const ExternalFindClosestNodesResponse = new ExternalFindClosestNodesResponse$Type();
1199
+ /**
1200
+ * @generated ServiceType for protobuf service dht.DhtNodeRpc
1201
+ */
1202
+ new runtimeRpc.ServiceType("dht.DhtNodeRpc", [
1203
+ { name: "getClosestPeers", options: {}, I: ClosestPeersRequest, O: ClosestPeersResponse },
1204
+ { name: "getClosestRingPeers", options: {}, I: ClosestRingPeersRequest, O: ClosestRingPeersResponse },
1205
+ { name: "ping", options: {}, I: PingRequest, O: PingResponse },
1206
+ { name: "leaveNotice", options: {}, I: LeaveNotice, O: Empty }
1207
+ ]);
1208
+ /**
1209
+ * @generated ServiceType for protobuf service dht.RouterRpc
1210
+ */
1211
+ new runtimeRpc.ServiceType("dht.RouterRpc", [
1212
+ { name: "routeMessage", options: {}, I: RouteMessageWrapper, O: RouteMessageAck },
1213
+ { name: "forwardMessage", options: {}, I: RouteMessageWrapper, O: RouteMessageAck }
1214
+ ]);
1215
+ /**
1216
+ * @generated ServiceType for protobuf service dht.RecursiveOperationRpc
1217
+ */
1218
+ new runtimeRpc.ServiceType("dht.RecursiveOperationRpc", [
1219
+ { name: "routeRequest", options: {}, I: RouteMessageWrapper, O: RouteMessageAck }
1220
+ ]);
1221
+ /**
1222
+ * @generated ServiceType for protobuf service dht.StoreRpc
1223
+ */
1224
+ new runtimeRpc.ServiceType("dht.StoreRpc", [
1225
+ { name: "storeData", options: {}, I: StoreDataRequest, O: StoreDataResponse },
1226
+ { name: "replicateData", options: {}, I: ReplicateDataRequest, O: Empty }
1227
+ ]);
1228
+ /**
1229
+ * @generated ServiceType for protobuf service dht.RecursiveOperationSessionRpc
1230
+ */
1231
+ new runtimeRpc.ServiceType("dht.RecursiveOperationSessionRpc", [
1232
+ { name: "sendResponse", options: {}, I: RecursiveOperationResponse, O: Empty }
1233
+ ]);
1234
+ /**
1235
+ * @generated ServiceType for protobuf service dht.WebsocketClientConnectorRpc
1236
+ */
1237
+ new runtimeRpc.ServiceType("dht.WebsocketClientConnectorRpc", [
1238
+ { name: "requestConnection", options: {}, I: WebsocketConnectionRequest, O: Empty }
1239
+ ]);
1240
+ /**
1241
+ * @generated ServiceType for protobuf service dht.WebrtcConnectorRpc
1242
+ */
1243
+ new runtimeRpc.ServiceType("dht.WebrtcConnectorRpc", [
1244
+ { name: "requestConnection", options: {}, I: WebrtcConnectionRequest, O: Empty },
1245
+ { name: "rtcOffer", options: {}, I: RtcOffer, O: Empty },
1246
+ { name: "rtcAnswer", options: {}, I: RtcAnswer, O: Empty },
1247
+ { name: "iceCandidate", options: {}, I: IceCandidate, O: Empty }
1248
+ ]);
1249
+ /**
1250
+ * @generated ServiceType for protobuf service dht.ConnectionLockRpc
1251
+ */
1252
+ new runtimeRpc.ServiceType("dht.ConnectionLockRpc", [
1253
+ { name: "lockRequest", options: {}, I: LockRequest, O: LockResponse },
1254
+ { name: "unlockRequest", options: {}, I: UnlockRequest, O: Empty },
1255
+ { name: "gracefulDisconnect", options: {}, I: DisconnectNotice, O: Empty },
1256
+ { name: "setPrivate", options: {}, I: SetPrivateRequest, O: Empty }
1257
+ ]);
1258
+ /**
1259
+ * @generated ServiceType for protobuf service dht.ExternalApiRpc
1260
+ */
1261
+ new runtimeRpc.ServiceType("dht.ExternalApiRpc", [
1262
+ { name: "externalFetchData", options: {}, I: ExternalFetchDataRequest, O: ExternalFetchDataResponse },
1263
+ { name: "externalStoreData", options: {}, I: ExternalStoreDataRequest, O: ExternalStoreDataResponse },
1264
+ { name: "externalFindClosestNodes", options: {}, I: ExternalFindClosestNodesRequest, O: ExternalFindClosestNodesResponse }
1265
+ ]);
1266
+
1267
+ // @generated by protobuf-ts 2.11.1 with parameter server_generic,generate_dependencies,long_type_number
1268
+ // @generated from protobuf file "packages/trackerless-network/protos/NetworkRpc.proto" (syntax proto3)
1269
+ // tslint:disable
1270
+ /**
1271
+ * @generated from protobuf enum ContentType
1272
+ */
1273
+ exports.ContentType = void 0;
1274
+ (function (ContentType) {
1275
+ /**
1276
+ * @generated from protobuf enum value: JSON = 0;
1277
+ */
1278
+ ContentType[ContentType["JSON"] = 0] = "JSON";
1279
+ /**
1280
+ * @generated from protobuf enum value: BINARY = 1;
1281
+ */
1282
+ ContentType[ContentType["BINARY"] = 1] = "BINARY";
1283
+ })(exports.ContentType || (exports.ContentType = {}));
1284
+ /**
1285
+ * @generated from protobuf enum EncryptionType
1286
+ */
1287
+ exports.EncryptionType = void 0;
1288
+ (function (EncryptionType) {
1289
+ /**
1290
+ * @generated from protobuf enum value: NONE = 0;
1291
+ */
1292
+ EncryptionType[EncryptionType["NONE"] = 0] = "NONE";
1293
+ /**
1294
+ * @generated from protobuf enum value: AES = 1;
1295
+ */
1296
+ EncryptionType[EncryptionType["AES"] = 1] = "AES";
1297
+ })(exports.EncryptionType || (exports.EncryptionType = {}));
1298
+ /**
1299
+ * @generated from protobuf enum AsymmetricEncryptionType
1300
+ */
1301
+ exports.AsymmetricEncryptionType = void 0;
1302
+ (function (AsymmetricEncryptionType) {
1303
+ /**
1304
+ * default
1305
+ *
1306
+ * @generated from protobuf enum value: RSA = 0;
1307
+ */
1308
+ AsymmetricEncryptionType[AsymmetricEncryptionType["RSA"] = 0] = "RSA";
1309
+ /**
1310
+ * @generated from protobuf enum value: ML_KEM = 1;
1311
+ */
1312
+ AsymmetricEncryptionType[AsymmetricEncryptionType["ML_KEM"] = 1] = "ML_KEM";
1313
+ })(exports.AsymmetricEncryptionType || (exports.AsymmetricEncryptionType = {}));
1314
+ /**
1315
+ * @generated from protobuf enum SignatureType
1316
+ */
1317
+ exports.SignatureType = void 0;
1318
+ (function (SignatureType) {
1319
+ /**
1320
+ * @generated from protobuf enum value: ECDSA_SECP256K1_LEGACY = 0;
1321
+ */
1322
+ SignatureType[SignatureType["ECDSA_SECP256K1_LEGACY"] = 0] = "ECDSA_SECP256K1_LEGACY";
1323
+ /**
1324
+ * @generated from protobuf enum value: ECDSA_SECP256K1_EVM = 1;
1325
+ */
1326
+ SignatureType[SignatureType["ECDSA_SECP256K1_EVM"] = 1] = "ECDSA_SECP256K1_EVM";
1327
+ /**
1328
+ * @generated from protobuf enum value: ERC_1271 = 2;
1329
+ */
1330
+ SignatureType[SignatureType["ERC_1271"] = 2] = "ERC_1271";
1331
+ /**
1332
+ * @generated from protobuf enum value: ML_DSA_87 = 3;
1333
+ */
1334
+ SignatureType[SignatureType["ML_DSA_87"] = 3] = "ML_DSA_87";
1335
+ /**
1336
+ * @generated from protobuf enum value: ECDSA_SECP256R1 = 4;
1337
+ */
1338
+ SignatureType[SignatureType["ECDSA_SECP256R1"] = 4] = "ECDSA_SECP256R1";
1339
+ })(exports.SignatureType || (exports.SignatureType = {}));
1340
+ /**
1341
+ * @generated from protobuf enum ProxyDirection
1342
+ */
1343
+ exports.ProxyDirection = void 0;
1344
+ (function (ProxyDirection) {
1345
+ /**
1346
+ * @generated from protobuf enum value: PUBLISH = 0;
1347
+ */
1348
+ ProxyDirection[ProxyDirection["PUBLISH"] = 0] = "PUBLISH";
1349
+ /**
1350
+ * @generated from protobuf enum value: SUBSCRIBE = 1;
1351
+ */
1352
+ ProxyDirection[ProxyDirection["SUBSCRIBE"] = 1] = "SUBSCRIBE";
1353
+ })(exports.ProxyDirection || (exports.ProxyDirection = {}));
1354
+ // @generated message type with reflection information, may provide speed optimized methods
1355
+ class MessageID$Type extends runtime.MessageType {
1356
+ constructor() {
1357
+ super("MessageID", [
1358
+ { no: 1, name: "streamId", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
1359
+ { no: 2, name: "streamPartition", kind: "scalar", T: 5 /*ScalarType.INT32*/ },
1360
+ { no: 3, name: "timestamp", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 2 /*LongType.NUMBER*/ },
1361
+ { no: 4, name: "sequenceNumber", kind: "scalar", T: 5 /*ScalarType.INT32*/ },
1362
+ { no: 5, name: "publisherId", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
1363
+ { no: 6, name: "messageChainId", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
1364
+ ]);
1365
+ }
1366
+ }
1367
+ /**
1368
+ * @generated MessageType for protobuf message MessageID
1369
+ */
1370
+ const MessageID = new MessageID$Type();
1371
+ // @generated message type with reflection information, may provide speed optimized methods
1372
+ class MessageRef$Type extends runtime.MessageType {
1373
+ constructor() {
1374
+ super("MessageRef", [
1375
+ { no: 1, name: "timestamp", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 2 /*LongType.NUMBER*/ },
1376
+ { no: 2, name: "sequenceNumber", kind: "scalar", T: 5 /*ScalarType.INT32*/ }
1377
+ ]);
1378
+ }
1379
+ }
1380
+ /**
1381
+ * @generated MessageType for protobuf message MessageRef
1382
+ */
1383
+ const MessageRef = new MessageRef$Type();
1384
+ // @generated message type with reflection information, may provide speed optimized methods
1385
+ class StreamMessage$Type extends runtime.MessageType {
1386
+ constructor() {
1387
+ super("StreamMessage", [
1388
+ { no: 1, name: "messageId", kind: "message", T: () => MessageID },
1389
+ { no: 2, name: "previousMessageRef", kind: "message", T: () => MessageRef },
1390
+ { no: 3, name: "signature", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
1391
+ { no: 4, name: "signatureType", kind: "enum", T: () => ["SignatureType", exports.SignatureType] },
1392
+ { no: 5, name: "contentMessage", kind: "message", oneof: "body", T: () => ContentMessage },
1393
+ { no: 6, name: "groupKeyRequest", kind: "message", oneof: "body", T: () => GroupKeyRequest },
1394
+ { no: 7, name: "groupKeyResponse", kind: "message", oneof: "body", T: () => GroupKeyResponse }
1395
+ ]);
1396
+ }
1397
+ }
1398
+ /**
1399
+ * @generated MessageType for protobuf message StreamMessage
1400
+ */
1401
+ const StreamMessage = new StreamMessage$Type();
1402
+ // @generated message type with reflection information, may provide speed optimized methods
1403
+ class ContentMessage$Type extends runtime.MessageType {
1404
+ constructor() {
1405
+ super("ContentMessage", [
1406
+ { no: 1, name: "content", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
1407
+ { no: 2, name: "contentType", kind: "enum", T: () => ["ContentType", exports.ContentType] },
1408
+ { no: 3, name: "encryptionType", kind: "enum", T: () => ["EncryptionType", exports.EncryptionType] },
1409
+ { no: 4, name: "groupKeyId", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ },
1410
+ { no: 5, name: "newGroupKey", kind: "message", T: () => EncryptedGroupKey }
1411
+ ]);
1412
+ }
1413
+ }
1414
+ /**
1415
+ * @generated MessageType for protobuf message ContentMessage
1416
+ */
1417
+ const ContentMessage = new ContentMessage$Type();
1418
+ // @generated message type with reflection information, may provide speed optimized methods
1419
+ class GroupKeyRequest$Type extends runtime.MessageType {
1420
+ constructor() {
1421
+ super("GroupKeyRequest", [
1422
+ { no: 1, name: "requestId", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
1423
+ { no: 2, name: "recipientId", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
1424
+ { no: 3, name: "publicKey", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
1425
+ { no: 4, name: "groupKeyIds", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ },
1426
+ { no: 5, name: "encryptionType", kind: "enum", T: () => ["AsymmetricEncryptionType", exports.AsymmetricEncryptionType] }
1427
+ ]);
1428
+ }
1429
+ }
1430
+ /**
1431
+ * @generated MessageType for protobuf message GroupKeyRequest
1432
+ */
1433
+ const GroupKeyRequest = new GroupKeyRequest$Type();
1434
+ // @generated message type with reflection information, may provide speed optimized methods
1435
+ class GroupKeyResponse$Type extends runtime.MessageType {
1436
+ constructor() {
1437
+ super("GroupKeyResponse", [
1438
+ { no: 1, name: "requestId", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
1439
+ { no: 2, name: "recipientId", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
1440
+ { no: 3, name: "groupKeys", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => EncryptedGroupKey },
1441
+ { no: 4, name: "encryptionType", kind: "enum", T: () => ["AsymmetricEncryptionType", exports.AsymmetricEncryptionType] }
1442
+ ]);
1443
+ }
1444
+ }
1445
+ /**
1446
+ * @generated MessageType for protobuf message GroupKeyResponse
1447
+ */
1448
+ const GroupKeyResponse = new GroupKeyResponse$Type();
1449
+ // @generated message type with reflection information, may provide speed optimized methods
1450
+ class EncryptedGroupKey$Type extends runtime.MessageType {
1451
+ constructor() {
1452
+ super("EncryptedGroupKey", [
1453
+ { no: 1, name: "id", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
1454
+ { no: 2, name: "data", kind: "scalar", T: 12 /*ScalarType.BYTES*/ }
1455
+ ]);
1456
+ }
1457
+ }
1458
+ /**
1459
+ * @generated MessageType for protobuf message EncryptedGroupKey
1460
+ */
1461
+ const EncryptedGroupKey = new EncryptedGroupKey$Type();
1462
+ // @generated message type with reflection information, may provide speed optimized methods
1463
+ class StreamPartHandshakeRequest$Type extends runtime.MessageType {
1464
+ constructor() {
1465
+ super("StreamPartHandshakeRequest", [
1466
+ { no: 1, name: "streamPartId", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
1467
+ { no: 2, name: "requestId", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
1468
+ { no: 3, name: "concurrentHandshakeNodeId", kind: "scalar", opt: true, T: 12 /*ScalarType.BYTES*/ },
1469
+ { no: 4, name: "neighborNodeIds", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 12 /*ScalarType.BYTES*/ },
1470
+ { no: 5, name: "interleaveNodeId", kind: "scalar", opt: true, T: 12 /*ScalarType.BYTES*/ }
1471
+ ]);
1472
+ }
1473
+ }
1474
+ /**
1475
+ * @generated MessageType for protobuf message StreamPartHandshakeRequest
1476
+ */
1477
+ const StreamPartHandshakeRequest = new StreamPartHandshakeRequest$Type();
1478
+ // @generated message type with reflection information, may provide speed optimized methods
1479
+ class StreamPartHandshakeResponse$Type extends runtime.MessageType {
1480
+ constructor() {
1481
+ super("StreamPartHandshakeResponse", [
1482
+ { no: 1, name: "accepted", kind: "scalar", T: 8 /*ScalarType.BOOL*/ },
1483
+ { no: 2, name: "requestId", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
1484
+ { no: 3, name: "interleaveTargetDescriptor", kind: "message", T: () => PeerDescriptor }
1485
+ ]);
1486
+ }
1487
+ }
1488
+ /**
1489
+ * @generated MessageType for protobuf message StreamPartHandshakeResponse
1490
+ */
1491
+ const StreamPartHandshakeResponse = new StreamPartHandshakeResponse$Type();
1492
+ // @generated message type with reflection information, may provide speed optimized methods
1493
+ class InterleaveRequest$Type extends runtime.MessageType {
1494
+ constructor() {
1495
+ super("InterleaveRequest", [
1496
+ { no: 1, name: "interleaveTargetDescriptor", kind: "message", T: () => PeerDescriptor }
1497
+ ]);
1498
+ }
1499
+ }
1500
+ /**
1501
+ * @generated MessageType for protobuf message InterleaveRequest
1502
+ */
1503
+ const InterleaveRequest = new InterleaveRequest$Type();
1504
+ // @generated message type with reflection information, may provide speed optimized methods
1505
+ class InterleaveResponse$Type extends runtime.MessageType {
1506
+ constructor() {
1507
+ super("InterleaveResponse", [
1508
+ { no: 1, name: "accepted", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }
1509
+ ]);
1510
+ }
1511
+ }
1512
+ /**
1513
+ * @generated MessageType for protobuf message InterleaveResponse
1514
+ */
1515
+ const InterleaveResponse = new InterleaveResponse$Type();
1516
+ // @generated message type with reflection information, may provide speed optimized methods
1517
+ class LeaveStreamPartNotice$Type extends runtime.MessageType {
1518
+ constructor() {
1519
+ super("LeaveStreamPartNotice", [
1520
+ { no: 1, name: "streamPartId", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
1521
+ { no: 2, name: "isEntryPoint", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }
1522
+ ]);
1523
+ }
1524
+ }
1525
+ /**
1526
+ * @generated MessageType for protobuf message LeaveStreamPartNotice
1527
+ */
1528
+ const LeaveStreamPartNotice = new LeaveStreamPartNotice$Type();
1529
+ // @generated message type with reflection information, may provide speed optimized methods
1530
+ class NeighborUpdate$Type extends runtime.MessageType {
1531
+ constructor() {
1532
+ super("NeighborUpdate", [
1533
+ { no: 1, name: "streamPartId", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
1534
+ { no: 2, name: "removeMe", kind: "scalar", T: 8 /*ScalarType.BOOL*/ },
1535
+ { no: 3, name: "neighborDescriptors", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => PeerDescriptor }
1536
+ ]);
1537
+ }
1538
+ }
1539
+ /**
1540
+ * @generated MessageType for protobuf message NeighborUpdate
1541
+ */
1542
+ const NeighborUpdate = new NeighborUpdate$Type();
1543
+ // @generated message type with reflection information, may provide speed optimized methods
1544
+ class ProxyConnectionRequest$Type extends runtime.MessageType {
1545
+ constructor() {
1546
+ super("ProxyConnectionRequest", [
1547
+ { no: 1, name: "direction", kind: "enum", opt: true, T: () => ["ProxyDirection", exports.ProxyDirection] },
1548
+ { no: 2, name: "userId", kind: "scalar", T: 12 /*ScalarType.BYTES*/ }
1549
+ ]);
1550
+ }
1551
+ }
1552
+ /**
1553
+ * @generated MessageType for protobuf message ProxyConnectionRequest
1554
+ */
1555
+ const ProxyConnectionRequest = new ProxyConnectionRequest$Type();
1556
+ // @generated message type with reflection information, may provide speed optimized methods
1557
+ class ProxyConnectionResponse$Type extends runtime.MessageType {
1558
+ constructor() {
1559
+ super("ProxyConnectionResponse", [
1560
+ { no: 1, name: "accepted", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }
1561
+ ]);
1562
+ }
1563
+ }
1564
+ /**
1565
+ * @generated MessageType for protobuf message ProxyConnectionResponse
1566
+ */
1567
+ const ProxyConnectionResponse = new ProxyConnectionResponse$Type();
1568
+ // @generated message type with reflection information, may provide speed optimized methods
1569
+ class TemporaryConnectionRequest$Type extends runtime.MessageType {
1570
+ constructor() {
1571
+ super("TemporaryConnectionRequest", []);
1572
+ }
1573
+ }
1574
+ /**
1575
+ * @generated MessageType for protobuf message TemporaryConnectionRequest
1576
+ */
1577
+ const TemporaryConnectionRequest = new TemporaryConnectionRequest$Type();
1578
+ // @generated message type with reflection information, may provide speed optimized methods
1579
+ class TemporaryConnectionResponse$Type extends runtime.MessageType {
1580
+ constructor() {
1581
+ super("TemporaryConnectionResponse", [
1582
+ { no: 1, name: "accepted", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }
1583
+ ]);
1584
+ }
1585
+ }
1586
+ /**
1587
+ * @generated MessageType for protobuf message TemporaryConnectionResponse
1588
+ */
1589
+ const TemporaryConnectionResponse = new TemporaryConnectionResponse$Type();
1590
+ // @generated message type with reflection information, may provide speed optimized methods
1591
+ class CloseTemporaryConnection$Type extends runtime.MessageType {
1592
+ constructor() {
1593
+ super("CloseTemporaryConnection", []);
1594
+ }
1595
+ }
1596
+ /**
1597
+ * @generated MessageType for protobuf message CloseTemporaryConnection
1598
+ */
1599
+ const CloseTemporaryConnection = new CloseTemporaryConnection$Type();
1600
+ // @generated message type with reflection information, may provide speed optimized methods
1601
+ class StreamPartitionInfo$Type extends runtime.MessageType {
1602
+ constructor() {
1603
+ super("StreamPartitionInfo", [
1604
+ { no: 1, name: "id", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
1605
+ { no: 2, name: "controlLayerNeighbors", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => PeerDescriptor },
1606
+ { no: 3, name: "deprecatedContentDeliveryLayerNeighbors", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => PeerDescriptor },
1607
+ { no: 4, name: "contentDeliveryLayerNeighbors", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => ContentDeliveryLayerNeighborInfo }
1608
+ ]);
1609
+ }
1610
+ }
1611
+ /**
1612
+ * @generated MessageType for protobuf message StreamPartitionInfo
1613
+ */
1614
+ const StreamPartitionInfo = new StreamPartitionInfo$Type();
1615
+ // @generated message type with reflection information, may provide speed optimized methods
1616
+ class ContentDeliveryLayerNeighborInfo$Type extends runtime.MessageType {
1617
+ constructor() {
1618
+ super("ContentDeliveryLayerNeighborInfo", [
1619
+ { no: 1, name: "peerDescriptor", kind: "message", T: () => PeerDescriptor },
1620
+ { no: 2, name: "rtt", kind: "scalar", opt: true, T: 5 /*ScalarType.INT32*/ }
1621
+ ]);
1622
+ }
1623
+ }
1624
+ /**
1625
+ * @generated MessageType for protobuf message ContentDeliveryLayerNeighborInfo
1626
+ */
1627
+ const ContentDeliveryLayerNeighborInfo = new ContentDeliveryLayerNeighborInfo$Type();
1628
+ // @generated message type with reflection information, may provide speed optimized methods
1629
+ class ControlLayerInfo$Type extends runtime.MessageType {
1630
+ constructor() {
1631
+ super("ControlLayerInfo", [
1632
+ { no: 1, name: "neighbors", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => PeerDescriptor },
1633
+ { no: 2, name: "connections", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => PeerDescriptor }
1634
+ ]);
1635
+ }
1636
+ }
1637
+ /**
1638
+ * @generated MessageType for protobuf message ControlLayerInfo
1639
+ */
1640
+ const ControlLayerInfo = new ControlLayerInfo$Type();
1641
+ // @generated message type with reflection information, may provide speed optimized methods
1642
+ class NodeInfoRequest$Type extends runtime.MessageType {
1643
+ constructor() {
1644
+ super("NodeInfoRequest", []);
1645
+ }
1646
+ }
1647
+ /**
1648
+ * @generated MessageType for protobuf message NodeInfoRequest
1649
+ */
1650
+ const NodeInfoRequest = new NodeInfoRequest$Type();
1651
+ // @generated message type with reflection information, may provide speed optimized methods
1652
+ class NodeInfoResponse$Type extends runtime.MessageType {
1653
+ constructor() {
1654
+ super("NodeInfoResponse", [
1655
+ { no: 1, name: "peerDescriptor", kind: "message", T: () => PeerDescriptor },
1656
+ { no: 2, name: "streamPartitions", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => StreamPartitionInfo },
1657
+ { no: 3, name: "controlLayer", kind: "message", T: () => ControlLayerInfo },
1658
+ { no: 4, name: "applicationVersion", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
1659
+ ]);
1660
+ }
1661
+ }
1662
+ /**
1663
+ * @generated MessageType for protobuf message NodeInfoResponse
1664
+ */
1665
+ const NodeInfoResponse = new NodeInfoResponse$Type();
1666
+ // @generated message type with reflection information, may provide speed optimized methods
1667
+ class PauseNeighborRequest$Type extends runtime.MessageType {
1668
+ constructor() {
1669
+ super("PauseNeighborRequest", [
1670
+ { no: 1, name: "messageChainId", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
1671
+ ]);
1672
+ }
1673
+ }
1674
+ /**
1675
+ * @generated MessageType for protobuf message PauseNeighborRequest
1676
+ */
1677
+ const PauseNeighborRequest = new PauseNeighborRequest$Type();
1678
+ // @generated message type with reflection information, may provide speed optimized methods
1679
+ class ResumeNeighborRequest$Type extends runtime.MessageType {
1680
+ constructor() {
1681
+ super("ResumeNeighborRequest", [
1682
+ { no: 1, name: "messageChainId", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
1683
+ { no: 2, name: "fromTimestamp", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 2 /*LongType.NUMBER*/ }
1684
+ ]);
1685
+ }
1686
+ }
1687
+ /**
1688
+ * @generated MessageType for protobuf message ResumeNeighborRequest
1689
+ */
1690
+ const ResumeNeighborRequest = new ResumeNeighborRequest$Type();
1691
+ /**
1692
+ * @generated ServiceType for protobuf service ContentDeliveryRpc
1693
+ */
1694
+ const ContentDeliveryRpc = new runtimeRpc.ServiceType("ContentDeliveryRpc", [
1695
+ { name: "sendStreamMessage", options: {}, I: StreamMessage, O: Empty },
1696
+ { name: "leaveStreamPartNotice", options: {}, I: LeaveStreamPartNotice, O: Empty }
1697
+ ]);
1698
+ /**
1699
+ * @generated ServiceType for protobuf service ProxyConnectionRpc
1700
+ */
1701
+ const ProxyConnectionRpc = new runtimeRpc.ServiceType("ProxyConnectionRpc", [
1702
+ { name: "requestConnection", options: {}, I: ProxyConnectionRequest, O: ProxyConnectionResponse }
1703
+ ]);
1704
+ /**
1705
+ * @generated ServiceType for protobuf service HandshakeRpc
1706
+ */
1707
+ const HandshakeRpc = new runtimeRpc.ServiceType("HandshakeRpc", [
1708
+ { name: "handshake", options: {}, I: StreamPartHandshakeRequest, O: StreamPartHandshakeResponse },
1709
+ { name: "interleaveRequest", options: {}, I: InterleaveRequest, O: InterleaveResponse }
1710
+ ]);
1711
+ /**
1712
+ * @generated ServiceType for protobuf service NeighborUpdateRpc
1713
+ */
1714
+ const NeighborUpdateRpc = new runtimeRpc.ServiceType("NeighborUpdateRpc", [
1715
+ { name: "neighborUpdate", options: {}, I: NeighborUpdate, O: NeighborUpdate }
1716
+ ]);
1717
+ /**
1718
+ * @generated ServiceType for protobuf service TemporaryConnectionRpc
1719
+ */
1720
+ const TemporaryConnectionRpc = new runtimeRpc.ServiceType("TemporaryConnectionRpc", [
1721
+ { name: "openConnection", options: {}, I: TemporaryConnectionRequest, O: TemporaryConnectionResponse },
1722
+ { name: "closeConnection", options: {}, I: CloseTemporaryConnection, O: Empty }
1723
+ ]);
1724
+ /**
1725
+ * @generated ServiceType for protobuf service NodeInfoRpc
1726
+ */
1727
+ const NodeInfoRpc = new runtimeRpc.ServiceType("NodeInfoRpc", [
1728
+ { name: "getInfo", options: {}, I: NodeInfoRequest, O: NodeInfoResponse }
1729
+ ]);
1730
+ /**
1731
+ * @generated ServiceType for protobuf service PlumtreeRpc
1732
+ */
1733
+ const PlumtreeRpc = new runtimeRpc.ServiceType("PlumtreeRpc", [
1734
+ { name: "pauseNeighbor", options: {}, I: PauseNeighborRequest, O: Empty },
1735
+ { name: "resumeNeighbor", options: {}, I: ResumeNeighborRequest, O: Empty },
1736
+ { name: "sendMetadata", options: {}, I: MessageID, O: Empty }
1737
+ ]);
1738
+
1739
+ // @generated by protobuf-ts 2.11.1 with parameter server_generic,generate_dependencies,long_type_number
1740
+ // @generated from protobuf file "packages/trackerless-network/protos/NetworkRpc.proto" (syntax proto3)
1741
+ // tslint:disable
1742
+ /**
1743
+ * @generated from protobuf service ContentDeliveryRpc
1744
+ */
1745
+ class ContentDeliveryRpcClient {
1746
+ _transport;
1747
+ typeName = ContentDeliveryRpc.typeName;
1748
+ methods = ContentDeliveryRpc.methods;
1749
+ options = ContentDeliveryRpc.options;
1750
+ constructor(_transport) {
1751
+ this._transport = _transport;
1752
+ }
1753
+ /**
1754
+ * @generated from protobuf rpc: sendStreamMessage
1755
+ */
1756
+ sendStreamMessage(input, options) {
1757
+ const method = this.methods[0], opt = this._transport.mergeOptions(options);
1758
+ return runtimeRpc.stackIntercept("unary", this._transport, method, opt, input);
1759
+ }
1760
+ /**
1761
+ * @generated from protobuf rpc: leaveStreamPartNotice
1762
+ */
1763
+ leaveStreamPartNotice(input, options) {
1764
+ const method = this.methods[1], opt = this._transport.mergeOptions(options);
1765
+ return runtimeRpc.stackIntercept("unary", this._transport, method, opt, input);
1766
+ }
1767
+ }
1768
+ /**
1769
+ * @generated from protobuf service ProxyConnectionRpc
1770
+ */
1771
+ class ProxyConnectionRpcClient {
1772
+ _transport;
1773
+ typeName = ProxyConnectionRpc.typeName;
1774
+ methods = ProxyConnectionRpc.methods;
1775
+ options = ProxyConnectionRpc.options;
1776
+ constructor(_transport) {
1777
+ this._transport = _transport;
1778
+ }
1779
+ /**
1780
+ * @generated from protobuf rpc: requestConnection
1781
+ */
1782
+ requestConnection(input, options) {
1783
+ const method = this.methods[0], opt = this._transport.mergeOptions(options);
1784
+ return runtimeRpc.stackIntercept("unary", this._transport, method, opt, input);
1785
+ }
1786
+ }
1787
+ /**
1788
+ * @generated from protobuf service HandshakeRpc
1789
+ */
1790
+ class HandshakeRpcClient {
1791
+ _transport;
1792
+ typeName = HandshakeRpc.typeName;
1793
+ methods = HandshakeRpc.methods;
1794
+ options = HandshakeRpc.options;
1795
+ constructor(_transport) {
1796
+ this._transport = _transport;
1797
+ }
1798
+ /**
1799
+ * @generated from protobuf rpc: handshake
1800
+ */
1801
+ handshake(input, options) {
1802
+ const method = this.methods[0], opt = this._transport.mergeOptions(options);
1803
+ return runtimeRpc.stackIntercept("unary", this._transport, method, opt, input);
1804
+ }
1805
+ /**
1806
+ * @generated from protobuf rpc: interleaveRequest
1807
+ */
1808
+ interleaveRequest(input, options) {
1809
+ const method = this.methods[1], opt = this._transport.mergeOptions(options);
1810
+ return runtimeRpc.stackIntercept("unary", this._transport, method, opt, input);
1811
+ }
1812
+ }
1813
+ /**
1814
+ * @generated from protobuf service NeighborUpdateRpc
1815
+ */
1816
+ class NeighborUpdateRpcClient {
1817
+ _transport;
1818
+ typeName = NeighborUpdateRpc.typeName;
1819
+ methods = NeighborUpdateRpc.methods;
1820
+ options = NeighborUpdateRpc.options;
1821
+ constructor(_transport) {
1822
+ this._transport = _transport;
1823
+ }
1824
+ /**
1825
+ * @generated from protobuf rpc: neighborUpdate
1826
+ */
1827
+ neighborUpdate(input, options) {
1828
+ const method = this.methods[0], opt = this._transport.mergeOptions(options);
1829
+ return runtimeRpc.stackIntercept("unary", this._transport, method, opt, input);
1830
+ }
1831
+ }
1832
+ /**
1833
+ * @generated from protobuf service TemporaryConnectionRpc
1834
+ */
1835
+ class TemporaryConnectionRpcClient {
1836
+ _transport;
1837
+ typeName = TemporaryConnectionRpc.typeName;
1838
+ methods = TemporaryConnectionRpc.methods;
1839
+ options = TemporaryConnectionRpc.options;
1840
+ constructor(_transport) {
1841
+ this._transport = _transport;
1842
+ }
1843
+ /**
1844
+ * @generated from protobuf rpc: openConnection
1845
+ */
1846
+ openConnection(input, options) {
1847
+ const method = this.methods[0], opt = this._transport.mergeOptions(options);
1848
+ return runtimeRpc.stackIntercept("unary", this._transport, method, opt, input);
1849
+ }
1850
+ /**
1851
+ * @generated from protobuf rpc: closeConnection
1852
+ */
1853
+ closeConnection(input, options) {
1854
+ const method = this.methods[1], opt = this._transport.mergeOptions(options);
1855
+ return runtimeRpc.stackIntercept("unary", this._transport, method, opt, input);
1856
+ }
1857
+ }
1858
+ /**
1859
+ * @generated from protobuf service NodeInfoRpc
1860
+ */
1861
+ class NodeInfoRpcClient {
1862
+ _transport;
1863
+ typeName = NodeInfoRpc.typeName;
1864
+ methods = NodeInfoRpc.methods;
1865
+ options = NodeInfoRpc.options;
1866
+ constructor(_transport) {
1867
+ this._transport = _transport;
1868
+ }
1869
+ /**
1870
+ * @generated from protobuf rpc: getInfo
1871
+ */
1872
+ getInfo(input, options) {
1873
+ const method = this.methods[0], opt = this._transport.mergeOptions(options);
1874
+ return runtimeRpc.stackIntercept("unary", this._transport, method, opt, input);
1875
+ }
1876
+ }
1877
+ /**
1878
+ * @generated from protobuf service PlumtreeRpc
1879
+ */
1880
+ class PlumtreeRpcClient {
1881
+ _transport;
1882
+ typeName = PlumtreeRpc.typeName;
1883
+ methods = PlumtreeRpc.methods;
1884
+ options = PlumtreeRpc.options;
1885
+ constructor(_transport) {
1886
+ this._transport = _transport;
1887
+ }
1888
+ /**
1889
+ * @generated from protobuf rpc: pauseNeighbor
1890
+ */
1891
+ pauseNeighbor(input, options) {
1892
+ const method = this.methods[0], opt = this._transport.mergeOptions(options);
1893
+ return runtimeRpc.stackIntercept("unary", this._transport, method, opt, input);
1894
+ }
1895
+ /**
1896
+ * @generated from protobuf rpc: resumeNeighbor
1897
+ */
1898
+ resumeNeighbor(input, options) {
1899
+ const method = this.methods[1], opt = this._transport.mergeOptions(options);
1900
+ return runtimeRpc.stackIntercept("unary", this._transport, method, opt, input);
1901
+ }
1902
+ /**
1903
+ * @generated from protobuf rpc: sendMetadata
1904
+ */
1905
+ sendMetadata(input, options) {
1906
+ const method = this.methods[2], opt = this._transport.mergeOptions(options);
1907
+ return runtimeRpc.stackIntercept("unary", this._transport, method, opt, input);
1908
+ }
1909
+ }
1910
+
1911
+ const logger$f = new utils.Logger('ContentDeliveryRpcRemote');
1912
+ class ContentDeliveryRpcRemote extends dht.RpcRemote {
1913
+ rtt;
1914
+ async sendStreamMessage(msg, bufferWhileConnecting) {
1915
+ const options = this.formDhtRpcOptions({
1916
+ notification: true,
1917
+ bufferWhileConnecting
1918
+ });
1919
+ this.getClient().sendStreamMessage(msg, options).catch(() => {
1920
+ logger$f.trace('Failed to sendStreamMessage');
1921
+ });
1922
+ }
1923
+ leaveStreamPartNotice(streamPartId, isLocalNodeEntryPoint) {
1924
+ const notification = {
1925
+ streamPartId,
1926
+ isEntryPoint: isLocalNodeEntryPoint
1927
+ };
1928
+ const options = this.formDhtRpcOptions({
1929
+ notification: true
1930
+ });
1931
+ this.getClient().leaveStreamPartNotice(notification, options).catch(() => {
1932
+ logger$f.debug('Failed to send leaveStreamPartNotice');
1933
+ });
1934
+ }
1935
+ setRtt(rtt) {
1936
+ this.rtt = rtt;
1937
+ }
1938
+ getRtt() {
1939
+ return this.rtt;
1940
+ }
1941
+ }
1942
+
1943
+ const logger$e = new utils.Logger('HandshakeRpcLocal');
1944
+ class HandshakeRpcLocal {
1945
+ options;
1946
+ constructor(options) {
1947
+ this.options = options;
1948
+ }
1949
+ async handshake(request, context) {
1950
+ return this.handleRequest(request, context);
1951
+ }
1952
+ handleRequest(request, context) {
1953
+ const senderDescriptor = context.incomingSourceDescriptor;
1954
+ const getInterleaveNodeIds = () => (request.interleaveNodeId !== undefined) ? [dht.toDhtAddress(request.interleaveNodeId)] : [];
1955
+ const senderNodeId = dht.toNodeId(senderDescriptor);
1956
+ if (this.options.ongoingInterleaves.has(senderNodeId)) {
1957
+ return this.rejectHandshake(request);
1958
+ }
1959
+ else if (this.options.neighbors.has(senderNodeId)
1960
+ || this.options.ongoingHandshakes.has(senderNodeId)) {
1961
+ return this.acceptHandshake(request, senderDescriptor);
1962
+ }
1963
+ else if (this.options.neighbors.size() + this.options.ongoingHandshakes.size < this.options.maxNeighborCount) {
1964
+ return this.acceptHandshake(request, senderDescriptor);
1965
+ }
1966
+ else if (this.options.neighbors.size(getInterleaveNodeIds()) - this.options.ongoingInterleaves.size >= 2
1967
+ && this.options.neighbors.size() <= this.options.maxNeighborCount) {
1968
+ // Do not accept the handshakes requests if the target neighbor count can potentially drop below 2
1969
+ // due to interleaving. This ensures that a stable number of connections is kept during high churn.
1970
+ return this.acceptHandshakeWithInterleaving(request, senderDescriptor);
1971
+ }
1972
+ else {
1973
+ return this.rejectHandshake(request);
1974
+ }
1975
+ }
1976
+ acceptHandshake(request, requester) {
1977
+ const res = {
1978
+ requestId: request.requestId,
1979
+ accepted: true
1980
+ };
1981
+ this.options.neighbors.add(this.options.createContentDeliveryRpcRemote(requester));
1982
+ return res;
1983
+ }
1984
+ // eslint-disable-next-line class-methods-use-this
1985
+ rejectHandshake(request) {
1986
+ const res = {
1987
+ requestId: request.requestId,
1988
+ accepted: false
1989
+ };
1990
+ return res;
1991
+ }
1992
+ acceptHandshakeWithInterleaving(request, requester) {
1993
+ const exclude = [];
1994
+ request.neighborNodeIds.forEach((id) => exclude.push(dht.toDhtAddress(id)));
1995
+ this.options.ongoingInterleaves.forEach((id) => exclude.push(id));
1996
+ exclude.push(dht.toNodeId(requester));
1997
+ if (request.interleaveNodeId !== undefined) {
1998
+ exclude.push(dht.toDhtAddress(request.interleaveNodeId));
1999
+ }
2000
+ const last = this.options.neighbors.getLast(exclude);
2001
+ const lastPeerDescriptor = last ? last.getPeerDescriptor() : undefined;
2002
+ if (last) {
2003
+ const nodeId = dht.toNodeId(last.getPeerDescriptor());
2004
+ const remote = this.options.createRpcRemote(last.getPeerDescriptor());
2005
+ this.options.ongoingInterleaves.add(nodeId);
2006
+ // Run this with then catch instead of setImmediate to avoid changes in state
2007
+ // eslint-disable-next-line promise/catch-or-return
2008
+ remote.interleaveRequest(requester).then((response) => {
2009
+ // If response is accepted, remove the last node from the target neighbors
2010
+ // and unlock the connection
2011
+ // If response is not accepted, keep the last node as a neighbor
2012
+ if (response.accepted) {
2013
+ this.options.neighbors.remove(dht.toNodeId(lastPeerDescriptor));
2014
+ }
2015
+ }).catch(() => {
2016
+ // no-op: InterleaveRequest cannot reject
2017
+ }).finally(() => {
2018
+ this.options.ongoingInterleaves.delete(nodeId);
2019
+ });
2020
+ }
2021
+ this.options.neighbors.add(this.options.createContentDeliveryRpcRemote(requester));
2022
+ return {
2023
+ requestId: request.requestId,
2024
+ accepted: true,
2025
+ interleaveTargetDescriptor: lastPeerDescriptor
2026
+ };
2027
+ }
2028
+ async interleaveRequest(message, context) {
2029
+ const senderPeerDescriptor = context.incomingSourceDescriptor;
2030
+ const remoteNodeId = dht.toNodeId(senderPeerDescriptor);
2031
+ try {
2032
+ await this.options.handshakeWithInterleaving(message.interleaveTargetDescriptor, remoteNodeId);
2033
+ this.options.neighbors.remove(remoteNodeId);
2034
+ return { accepted: true };
2035
+ }
2036
+ catch (err) {
2037
+ logger$e.debug(`interleaveRequest to ${dht.toNodeId(message.interleaveTargetDescriptor)} failed`, { err });
2038
+ return { accepted: false };
2039
+ }
2040
+ }
2041
+ }
2042
+
2043
+ const logger$d = new utils.Logger('HandshakeRpcRemote');
2044
+ const INTERLEAVE_REQUEST_TIMEOUT = 10000;
2045
+ class HandshakeRpcRemote extends dht.RpcRemote {
2046
+ async handshake(streamPartId, neighborNodeIds, concurrentHandshakeNodeId, interleaveNodeId) {
2047
+ const request = {
2048
+ streamPartId,
2049
+ requestId: uuid.v4(),
2050
+ neighborNodeIds: neighborNodeIds.map((id) => dht.toDhtAddressRaw(id)),
2051
+ concurrentHandshakeNodeId: (concurrentHandshakeNodeId !== undefined) ? dht.toDhtAddressRaw(concurrentHandshakeNodeId) : undefined,
2052
+ interleaveNodeId: (interleaveNodeId !== undefined) ? dht.toDhtAddressRaw(interleaveNodeId) : undefined
2053
+ };
2054
+ try {
2055
+ const response = await this.getClient().handshake(request, this.formDhtRpcOptions());
2056
+ return {
2057
+ accepted: response.accepted,
2058
+ interleaveTargetDescriptor: response.interleaveTargetDescriptor
2059
+ };
2060
+ }
2061
+ catch (err) {
2062
+ logger$d.debug(`handshake to ${dht.toNodeId(this.getPeerDescriptor())} failed`, { err });
2063
+ return {
2064
+ accepted: false
2065
+ };
2066
+ }
2067
+ }
2068
+ async interleaveRequest(originatorDescriptor) {
2069
+ const request = {
2070
+ interleaveTargetDescriptor: originatorDescriptor
2071
+ };
2072
+ const options = this.formDhtRpcOptions({
2073
+ connect: false,
2074
+ timeout: INTERLEAVE_REQUEST_TIMEOUT
2075
+ });
2076
+ try {
2077
+ const res = await this.getClient().interleaveRequest(request, options);
2078
+ return {
2079
+ accepted: res.accepted
2080
+ };
2081
+ }
2082
+ catch (err) {
2083
+ logger$d.debug(`interleaveRequest to ${dht.toNodeId(this.getPeerDescriptor())} failed`, { err });
2084
+ return {
2085
+ accepted: false
2086
+ };
2087
+ }
2088
+ }
2089
+ }
2090
+
2091
+ const logger$c = new utils.Logger('Handshaker');
2092
+ const PARALLEL_HANDSHAKE_COUNT = 2;
2093
+ class Handshaker {
2094
+ options;
2095
+ rpcLocal;
2096
+ constructor(options) {
2097
+ this.options = options;
2098
+ this.rpcLocal = new HandshakeRpcLocal({
2099
+ streamPartId: this.options.streamPartId,
2100
+ neighbors: this.options.neighbors,
2101
+ ongoingHandshakes: this.options.ongoingHandshakes,
2102
+ ongoingInterleaves: new Set(),
2103
+ maxNeighborCount: this.options.maxNeighborCount,
2104
+ handshakeWithInterleaving: (target, remoteNodeId) => this.handshakeWithInterleaving(target, remoteNodeId),
2105
+ createRpcRemote: (target) => this.createRpcRemote(target),
2106
+ createContentDeliveryRpcRemote: (target) => this.createContentDeliveryRpcRemote(target)
2107
+ });
2108
+ this.options.rpcCommunicator.registerRpcMethod(InterleaveRequest, InterleaveResponse, 'interleaveRequest', (req, context) => this.rpcLocal.interleaveRequest(req, context), { timeout: INTERLEAVE_REQUEST_TIMEOUT });
2109
+ this.options.rpcCommunicator.registerRpcMethod(StreamPartHandshakeRequest, StreamPartHandshakeResponse, 'handshake', (req, context) => this.rpcLocal.handshake(req, context));
2110
+ }
2111
+ async attemptHandshakesOnContacts(excludedIds) {
2112
+ // TODO use options option or named constant? or why the value 2?
2113
+ if (this.options.neighbors.size() + this.options.ongoingHandshakes.size < this.options.maxNeighborCount - 2) {
2114
+ logger$c.trace(`Attempting parallel handshakes with ${PARALLEL_HANDSHAKE_COUNT} targets`);
2115
+ return this.selectParallelTargetsAndHandshake(excludedIds);
2116
+ }
2117
+ else if (this.options.neighbors.size() + this.options.ongoingHandshakes.size < this.options.maxNeighborCount) {
2118
+ logger$c.trace(`Attempting handshake with new target`);
2119
+ return this.selectNewTargetAndHandshake(excludedIds);
2120
+ }
2121
+ return excludedIds;
2122
+ }
2123
+ async selectParallelTargetsAndHandshake(excludedIds) {
2124
+ const exclude = excludedIds.concat(this.options.neighbors.getIds());
2125
+ const targets = this.selectParallelTargets(exclude);
2126
+ targets.forEach((contact) => this.options.ongoingHandshakes.add(dht.toNodeId(contact.getPeerDescriptor())));
2127
+ return this.doParallelHandshakes(targets, exclude);
2128
+ }
2129
+ selectParallelTargets(excludedIds) {
2130
+ const targets = new Map();
2131
+ const getExcludedIds = () => [...excludedIds, ...Array.from(targets.keys())];
2132
+ // Step 1: If no neighbors, try to find a WebSocket node first
2133
+ if (this.options.neighbors.size() === 0) {
2134
+ const wsNode = this.options.nearbyNodeView.getFirst(getExcludedIds(), true);
2135
+ if (wsNode) {
2136
+ const wsNodeId = dht.toNodeId(wsNode.getPeerDescriptor());
2137
+ targets.set(wsNodeId, wsNode);
2138
+ }
2139
+ }
2140
+ // Step 2: Add left and right contacts from the ring
2141
+ const left = this.options.leftNodeView.getFirst(getExcludedIds());
2142
+ const right = this.options.rightNodeView.getFirst(getExcludedIds());
2143
+ if (left) {
2144
+ targets.set(dht.toNodeId(left.getPeerDescriptor()), left);
2145
+ }
2146
+ if (right) {
2147
+ targets.set(dht.toNodeId(right.getPeerDescriptor()), right);
2148
+ }
2149
+ // Step 3: Add closest contact based on Kademlia metric if needed
2150
+ if (targets.size < PARALLEL_HANDSHAKE_COUNT) {
2151
+ const closest = this.options.nearbyNodeView.getFirst(getExcludedIds());
2152
+ if (closest) {
2153
+ targets.set(dht.toNodeId(closest.getPeerDescriptor()), closest);
2154
+ }
2155
+ }
2156
+ // Step 4: Fill remaining slots with random contacts
2157
+ while (targets.size < PARALLEL_HANDSHAKE_COUNT) {
2158
+ const random = this.options.randomNodeView.getRandom(getExcludedIds());
2159
+ if (!random) {
2160
+ break;
2161
+ }
2162
+ targets.set(dht.toNodeId(random.getPeerDescriptor()), random);
2163
+ }
2164
+ return Array.from(targets.values()).map((neighbor) => this.createRpcRemote(neighbor.getPeerDescriptor()));
2165
+ }
2166
+ async doParallelHandshakes(targets, excludedIds) {
2167
+ const results = await Promise.allSettled(Array.from(targets.values()).map(async (target, i) => {
2168
+ const otherNode = i === 0 ? targets[1] : targets[0];
2169
+ // TODO better check (currently this condition is always true)
2170
+ const otherNodeId = otherNode ? dht.toNodeId(otherNode.getPeerDescriptor()) : undefined;
2171
+ return this.handshakeWithTarget(target, otherNodeId);
2172
+ }));
2173
+ results.forEach((res, i) => {
2174
+ if (res.status !== 'fulfilled' || !res.value) {
2175
+ excludedIds.push(dht.toNodeId(targets[i].getPeerDescriptor()));
2176
+ }
2177
+ });
2178
+ return excludedIds;
2179
+ }
2180
+ async selectNewTargetAndHandshake(excludedIds) {
2181
+ const exclude = excludedIds.concat(this.options.neighbors.getIds());
2182
+ const target = this.options.leftNodeView.getFirst(exclude)
2183
+ ?? this.options.rightNodeView.getFirst(exclude)
2184
+ ?? this.options.nearbyNodeView.getFirst(exclude)
2185
+ ?? this.options.randomNodeView.getRandom(exclude);
2186
+ if (target) {
2187
+ const accepted = await this.handshakeWithTarget(this.createRpcRemote(target.getPeerDescriptor()));
2188
+ if (!accepted) {
2189
+ excludedIds.push(dht.toNodeId(target.getPeerDescriptor()));
2190
+ }
2191
+ }
2192
+ return excludedIds;
2193
+ }
2194
+ async handshakeWithTarget(target, concurrentNodeId) {
2195
+ const targetNodeId = dht.toNodeId(target.getPeerDescriptor());
2196
+ this.options.ongoingHandshakes.add(targetNodeId);
2197
+ const result = await target.handshake(this.options.streamPartId, this.options.neighbors.getIds(), concurrentNodeId);
2198
+ if (result.accepted) {
2199
+ this.options.neighbors.add(this.createContentDeliveryRpcRemote(target.getPeerDescriptor()));
2200
+ }
2201
+ if (result.interleaveTargetDescriptor) {
2202
+ await this.handshakeWithInterleaving(result.interleaveTargetDescriptor, targetNodeId);
2203
+ }
2204
+ this.options.ongoingHandshakes.delete(targetNodeId);
2205
+ return result.accepted;
2206
+ }
2207
+ async handshakeWithInterleaving(target, remoteNodeId) {
2208
+ const remote = this.createRpcRemote(target);
2209
+ const targetNodeId = dht.toNodeId(remote.getPeerDescriptor());
2210
+ this.options.ongoingHandshakes.add(targetNodeId);
2211
+ const result = await remote.handshake(this.options.streamPartId, this.options.neighbors.getIds(), undefined, remoteNodeId);
2212
+ if (result.accepted) {
2213
+ this.options.neighbors.add(this.createContentDeliveryRpcRemote(remote.getPeerDescriptor()));
2214
+ }
2215
+ this.options.ongoingHandshakes.delete(targetNodeId);
2216
+ return result.accepted;
2217
+ }
2218
+ createRpcRemote(targetPeerDescriptor) {
2219
+ return new HandshakeRpcRemote(this.options.localPeerDescriptor, targetPeerDescriptor, this.options.rpcCommunicator, HandshakeRpcClient, this.options.rpcRequestTimeout);
2220
+ }
2221
+ createContentDeliveryRpcRemote(targetPeerDescriptor) {
2222
+ return new ContentDeliveryRpcRemote(this.options.localPeerDescriptor, targetPeerDescriptor, this.options.rpcCommunicator, ContentDeliveryRpcClient, this.options.rpcRequestTimeout);
2223
+ }
2224
+ getOngoingHandshakes() {
2225
+ return this.options.ongoingHandshakes;
2226
+ }
2227
+ }
2228
+
2229
+ const INITIAL_WAIT = 100;
2230
+ const INTERVAL = 250;
2231
+ const logger$b = new utils.Logger('NeighborFinder');
2232
+ class NeighborFinder {
2233
+ abortController;
2234
+ options;
2235
+ running = false;
2236
+ constructor(options) {
2237
+ this.options = options;
2238
+ this.abortController = new AbortController();
2239
+ }
2240
+ async findNeighbors(excluded) {
2241
+ if (!this.running) {
2242
+ return;
2243
+ }
2244
+ const newExcludes = await this.options.doFindNeighbors(excluded);
2245
+ const uniqueContactCount = new Set([
2246
+ ...this.options.nearbyNodeView.getIds(),
2247
+ ...this.options.leftNodeView.getIds(),
2248
+ ...this.options.rightNodeView.getIds(),
2249
+ ...this.options.randomNodeView.getIds()
2250
+ ]).size;
2251
+ if (this.options.neighbors.size() < this.options.minCount && newExcludes.length < uniqueContactCount) {
2252
+ // TODO should we catch possible promise rejection?
2253
+ utils.setAbortableTimeout(() => this.findNeighbors(newExcludes), INTERVAL, this.abortController.signal);
2254
+ }
2255
+ else if (this.options.neighbors.size() === 0 && uniqueContactCount > 0) {
2256
+ logger$b.debug('No neighbors found yet contacts are available, restarting handshaking process');
2257
+ utils.setAbortableTimeout(() => this.findNeighbors([]), INTERVAL, this.abortController.signal);
2258
+ }
2259
+ else {
2260
+ this.running = false;
2261
+ }
2262
+ }
2263
+ isRunning() {
2264
+ return this.running;
2265
+ }
2266
+ start(excluded = []) {
2267
+ if (this.running) {
2268
+ return;
2269
+ }
2270
+ this.running = true;
2271
+ // TODO should we catch possible promise rejection?
2272
+ utils.setAbortableTimeout(async () => {
2273
+ await Promise.all([
2274
+ this.findNeighbors(excluded),
2275
+ this.findNeighbors(excluded)
2276
+ ]);
2277
+ }, INITIAL_WAIT, this.abortController.signal);
2278
+ }
2279
+ stop() {
2280
+ if (!this.running) {
2281
+ return;
2282
+ }
2283
+ this.running = false;
2284
+ this.abortController.abort();
2285
+ }
2286
+ }
2287
+
2288
+ class NeighborUpdateRpcLocal {
2289
+ options;
2290
+ constructor(options) {
2291
+ this.options = options;
2292
+ }
2293
+ updateContacts(neighborDescriptors) {
2294
+ const ownNodeId = dht.toNodeId(this.options.localPeerDescriptor);
2295
+ const newPeerDescriptors = neighborDescriptors.filter((peerDescriptor) => {
2296
+ const nodeId = dht.toNodeId(peerDescriptor);
2297
+ return nodeId !== ownNodeId && !this.options.neighbors.getIds().includes(nodeId);
2298
+ });
2299
+ newPeerDescriptors.forEach((peerDescriptor) => this.options.nearbyNodeView.add(new ContentDeliveryRpcRemote(this.options.localPeerDescriptor, peerDescriptor, this.options.rpcCommunicator, ContentDeliveryRpcClient)));
2300
+ }
2301
+ createResponse(removeMe) {
2302
+ return {
2303
+ streamPartId: this.options.streamPartId,
2304
+ neighborDescriptors: this.options.neighbors.getAll().map((neighbor) => neighbor.getPeerDescriptor()),
2305
+ removeMe
2306
+ };
2307
+ }
2308
+ // INeighborUpdateRpc server method
2309
+ async neighborUpdate(message, context) {
2310
+ const senderPeerDescriptor = context.incomingSourceDescriptor;
2311
+ const remoteNodeId = dht.toNodeId(senderPeerDescriptor);
2312
+ this.updateContacts(message.neighborDescriptors);
2313
+ if (!this.options.neighbors.has(remoteNodeId) && !this.options.ongoingHandshakes.has(remoteNodeId)) {
2314
+ return this.createResponse(true);
2315
+ }
2316
+ else {
2317
+ const isOverNeighborCount = this.options.neighbors.size() > this.options.neighborTargetCount
2318
+ // Motivation: We don't know the remote's neighborTargetCount setting here. We only ask to cut connections
2319
+ // if the remote has a "sufficient" number of neighbors, where "sufficient" means our neighborTargetCount
2320
+ // setting.
2321
+ && message.neighborDescriptors.length > this.options.neighborTargetCount;
2322
+ if (!isOverNeighborCount) {
2323
+ this.options.neighborFinder.start();
2324
+ }
2325
+ else {
2326
+ this.options.neighbors.remove(remoteNodeId);
2327
+ }
2328
+ return this.createResponse(isOverNeighborCount);
2329
+ }
2330
+ }
2331
+ }
2332
+
2333
+ const logger$a = new utils.Logger('NeighborUpdateRpcRemote');
2334
+ class NeighborUpdateRpcRemote extends dht.RpcRemote {
2335
+ async updateNeighbors(streamPartId, neighbors) {
2336
+ const request = {
2337
+ streamPartId,
2338
+ neighborDescriptors: neighbors,
2339
+ removeMe: false
2340
+ };
2341
+ try {
2342
+ const response = await this.getClient().neighborUpdate(request, this.formDhtRpcOptions());
2343
+ return {
2344
+ peerDescriptors: response.neighborDescriptors,
2345
+ removeMe: response.removeMe
2346
+ };
2347
+ }
2348
+ catch (err) {
2349
+ logger$a.debug(`updateNeighbors to ${dht.toNodeId(this.getPeerDescriptor())} failed`, { err });
2350
+ return {
2351
+ peerDescriptors: [],
2352
+ removeMe: true
2353
+ };
2354
+ }
2355
+ }
2356
+ }
2357
+
2358
+ const logger$9 = new utils.Logger('NeighborUpdateManager');
2359
+ const DEFAULT_NEIGHBOR_UPDATE_INTERVAL = 10 * 1000;
2360
+ class NeighborUpdateManager {
2361
+ abortController;
2362
+ options;
2363
+ rpcLocal;
2364
+ constructor(options) {
2365
+ this.abortController = new AbortController();
2366
+ this.rpcLocal = new NeighborUpdateRpcLocal(options);
2367
+ this.options = options;
2368
+ this.options.rpcCommunicator.registerRpcMethod(NeighborUpdate, NeighborUpdate, 'neighborUpdate', (req, context) => this.rpcLocal.neighborUpdate(req, context));
2369
+ }
2370
+ async start() {
2371
+ await utils.scheduleAtInterval(() => this.updateNeighborInfo(), this.options.neighborUpdateInterval, false, this.abortController.signal);
2372
+ }
2373
+ stop() {
2374
+ this.abortController.abort();
2375
+ }
2376
+ async updateNeighborInfo() {
2377
+ logger$9.trace(`Updating neighbor info to nodes`);
2378
+ const neighborDescriptors = this.options.neighbors.getAll().map((neighbor) => neighbor.getPeerDescriptor());
2379
+ const startTime = Date.now();
2380
+ await Promise.allSettled(this.options.neighbors.getAll().map(async (neighbor) => {
2381
+ const res = await this.createRemote(neighbor.getPeerDescriptor()).updateNeighbors(this.options.streamPartId, neighborDescriptors);
2382
+ const nodeId = dht.toNodeId(neighbor.getPeerDescriptor());
2383
+ this.options.neighbors.get(nodeId).setRtt(Date.now() - startTime);
2384
+ if (res.removeMe) {
2385
+ this.options.neighbors.remove(nodeId);
2386
+ this.options.neighborFinder.start([nodeId]);
2387
+ }
2388
+ }));
2389
+ }
2390
+ createRemote(targetPeerDescriptor) {
2391
+ return new NeighborUpdateRpcRemote(this.options.localPeerDescriptor, targetPeerDescriptor, this.options.rpcCommunicator, NeighborUpdateRpcClient);
2392
+ }
2393
+ }
2394
+
2395
+ class ContentDeliveryRpcLocal {
2396
+ options;
2397
+ constructor(options) {
2398
+ this.options = options;
2399
+ }
2400
+ async sendStreamMessage(message, context) {
2401
+ const previousNode = context.incomingSourceDescriptor;
2402
+ const previousNodeId = dht.toNodeId(previousNode);
2403
+ this.options.markForInspection(previousNodeId, message.messageId);
2404
+ if (this.options.plumtreeManager === undefined) {
2405
+ if (this.options.markAndCheckDuplicate(message.messageId, message.previousMessageRef)) {
2406
+ this.options.broadcast(message, previousNodeId);
2407
+ }
2408
+ }
2409
+ else if (this.options.markAndCheckDuplicate(message.messageId, message.previousMessageRef)) {
2410
+ // Message is not a duplicate, so we can broadcast it over the plumtree
2411
+ this.options.plumtreeManager.broadcast(message, previousNodeId);
2412
+ }
2413
+ else {
2414
+ // Message is a duplicate, so we need to pause the neighbor
2415
+ await this.options.plumtreeManager.pauseNeighbor(previousNode, message.messageId.messageChainId);
2416
+ }
2417
+ return Empty;
2418
+ }
2419
+ async leaveStreamPartNotice(message, context) {
2420
+ if (message.streamPartId === this.options.streamPartId) {
2421
+ const sourcePeerDescriptor = context.incomingSourceDescriptor;
2422
+ const remoteNodeId = dht.toNodeId(sourcePeerDescriptor);
2423
+ this.options.onLeaveNotice(remoteNodeId, message.isEntryPoint);
2424
+ }
2425
+ return Empty;
2426
+ }
2427
+ }
2428
+
2429
+ /**
2430
+ * Represent a pair of numbers (a,b). Ordering between two pairs is defined as
2431
+ * follows. First compare first numbers. Compare second numbers if first are
2432
+ * equal.
2433
+ */
2434
+ class NumberPair {
2435
+ a;
2436
+ b;
2437
+ constructor(a, b) {
2438
+ this.a = a;
2439
+ this.b = b;
2440
+ }
2441
+ greaterThanOrEqual(otherPair) {
2442
+ return this.greaterThan(otherPair) || this.equalTo(otherPair);
2443
+ }
2444
+ greaterThan(otherPair) {
2445
+ return this.compareTo(otherPair) === 1;
2446
+ }
2447
+ equalTo(otherPair) {
2448
+ return this.compareTo(otherPair) === 0;
2449
+ }
2450
+ compareTo(otherPair) {
2451
+ if (this.a > otherPair.a) {
2452
+ return 1;
2453
+ }
2454
+ if (this.a < otherPair.a) {
2455
+ return -1;
2456
+ }
2457
+ if (this.b > otherPair.b) {
2458
+ return 1;
2459
+ }
2460
+ if (this.b < otherPair.b) {
2461
+ return -1;
2462
+ }
2463
+ return 0;
2464
+ }
2465
+ toString() {
2466
+ return `${this.a}|${this.b}`;
2467
+ }
2468
+ }
2469
+ class InvalidNumberingError extends Error {
2470
+ constructor() {
2471
+ super('pre-condition: previousNumber < number');
2472
+ }
2473
+ }
2474
+ class GapMisMatchError extends Error {
2475
+ constructor(state, previousNumber, number) {
2476
+ super('pre-condition: gap overlap in given numbers:'
2477
+ + ` previousNumber=${previousNumber.toString()}, number=${number.toString()}, state=${state}`);
2478
+ }
2479
+ }
2480
+ /**
2481
+ *
2482
+ * Keeps track of a stream's message numbers and reports already seen numbers
2483
+ * as duplicates.
2484
+ *
2485
+ * Leverages the fact that message are assigned numbers from a strictly
2486
+ * increasing integer sequence for lowered space complexity. For example,
2487
+ * if we know that all messages up to number N have been seen, we can only
2488
+ * store the number N to provide message identity check. This is because
2489
+ * anything less than N can be deemed a duplicate.
2490
+ *
2491
+ * Messages arriving out-of-order makes this a bit harder since gaps form.
2492
+ * Most of the code in this class is built to deal with this complexity.
2493
+ * Basically, we need to keep track of which intervals [N,M] could still
2494
+ * contain unseen messages. We should also remove intervals after we are sure
2495
+ * that they contain no unseen messages.
2496
+ *
2497
+ * In addition to the above, there needs to be a limit to the number of
2498
+ * intervals we store, as it could well be that some messages never
2499
+ * arrive. The strategy is to start removing the lowest numbered
2500
+ * intervals when storage limits are hit.
2501
+ *
2502
+ */
2503
+ class DuplicateMessageDetector {
2504
+ maxGapCount;
2505
+ gaps;
2506
+ constructor(maxGapCount = 10000) {
2507
+ this.maxGapCount = maxGapCount;
2508
+ this.gaps = []; // ascending order of half-closed intervals (x,y] representing gaps that contain unseen message(s)
2509
+ }
2510
+ /**
2511
+ * returns true if number has not yet been seen (i.e. is not a duplicate)
2512
+ */
2513
+ markAndCheck(previousNumber, number) {
2514
+ if (previousNumber?.greaterThanOrEqual(number)) {
2515
+ throw new InvalidNumberingError();
2516
+ }
2517
+ if (this.gaps.length === 0) {
2518
+ this.gaps.push([number, new NumberPair(Infinity, Infinity)]);
2519
+ return true;
2520
+ }
2521
+ // Handle special case where previousNumber is not provided. Only
2522
+ // minimal duplicate detection is provided (comparing against latest
2523
+ // known message number).
2524
+ if (previousNumber === null) {
2525
+ if (number.greaterThan(this.gaps[this.gaps.length - 1][0])) {
2526
+ this.gaps[this.gaps.length - 1][0] = number;
2527
+ return true;
2528
+ }
2529
+ return false;
2530
+ }
2531
+ for (let i = this.gaps.length - 1; i >= 0; --i) {
2532
+ const [lowerBound, upperBound] = this.gaps[i]; // invariant: upperBound > lowerBound
2533
+ // implies number > upperBound (would've been handled in previous iteration if gap exists)
2534
+ if (previousNumber.greaterThanOrEqual(upperBound)) {
2535
+ return false;
2536
+ }
2537
+ if (previousNumber.greaterThanOrEqual(lowerBound)) {
2538
+ if (number.greaterThan(upperBound)) {
2539
+ throw new GapMisMatchError(this.toString(), previousNumber, number);
2540
+ }
2541
+ if (previousNumber.equalTo(lowerBound)) {
2542
+ if (number.equalTo(upperBound)) {
2543
+ this.gaps.splice(i, 1);
2544
+ }
2545
+ else {
2546
+ this.gaps[i] = [number, upperBound];
2547
+ }
2548
+ }
2549
+ else if (number.equalTo(upperBound)) {
2550
+ this.gaps[i] = [lowerBound, previousNumber];
2551
+ }
2552
+ else {
2553
+ this.gaps.splice(i, 1, [lowerBound, previousNumber], [number, upperBound]);
2554
+ }
2555
+ // invariants after:
2556
+ // - gaps are in ascending order
2557
+ // - the intersection between any two gaps is empty
2558
+ // - there are no gaps that define the empty set
2559
+ // - last gap is [n, Infinity]
2560
+ // - anything not covered by a gap is considered seen
2561
+ this.dropLowestGapIfOverMaxGapCount();
2562
+ return true;
2563
+ }
2564
+ if (number.greaterThan(lowerBound)) {
2565
+ throw new GapMisMatchError(this.toString(), previousNumber, number);
2566
+ }
2567
+ }
2568
+ return false;
2569
+ }
2570
+ dropLowestGapIfOverMaxGapCount() {
2571
+ // invariant: this.gaps.length <= this.maxGapCount + 1
2572
+ if (this.gaps.length > this.maxGapCount) {
2573
+ this.gaps.shift();
2574
+ }
2575
+ }
2576
+ toString() {
2577
+ return this.gaps.map(([lower, upper]) => `(${lower.toString()}, ${upper.toString()}]`).join(', ');
2578
+ }
2579
+ }
2580
+
2581
+ const markAndCheckDuplicate = (duplicateDetectors, currentMessage, previousMessageRef) => {
2582
+ const detectorKey = `${utils.toUserId(currentMessage.publisherId)}-${currentMessage.messageChainId}`;
2583
+ const previousNumberPair = previousMessageRef ?
2584
+ new NumberPair(Number(previousMessageRef.timestamp), previousMessageRef.sequenceNumber) : null;
2585
+ const currentNumberPair = new NumberPair(Number(currentMessage.timestamp), currentMessage.sequenceNumber);
2586
+ if (!duplicateDetectors.has(detectorKey)) {
2587
+ duplicateDetectors.set(detectorKey, new DuplicateMessageDetector());
2588
+ }
2589
+ return duplicateDetectors.get(detectorKey).markAndCheck(previousNumberPair, currentNumberPair);
2590
+ };
2591
+
2592
+ const DEFAULT_NODE_VIEW_SIZE = 20;
2593
+ const DEFAULT_NEIGHBOR_TARGET_COUNT = 4;
2594
+ const DEFAULT_ACCEPT_PROXY_CONNECTIONS = false;
2595
+ const logger$8 = new utils.Logger('ContentDeliveryLayerNode');
2596
+ class ContentDeliveryLayerNode extends eventemitter3.EventEmitter {
2597
+ started = false;
2598
+ duplicateDetectors;
2599
+ options;
2600
+ contentDeliveryRpcLocal;
2601
+ abortController = new AbortController();
2602
+ messagesPropagated = 0;
2603
+ constructor(options) {
2604
+ super();
2605
+ this.options = options;
2606
+ this.duplicateDetectors = new Map();
2607
+ this.contentDeliveryRpcLocal = new ContentDeliveryRpcLocal({
2608
+ localPeerDescriptor: this.options.localPeerDescriptor,
2609
+ streamPartId: this.options.streamPartId,
2610
+ rpcCommunicator: this.options.rpcCommunicator,
2611
+ markAndCheckDuplicate: (msg, prev) => markAndCheckDuplicate(this.duplicateDetectors, msg, prev),
2612
+ broadcast: (message, previousNode) => this.broadcast(message, previousNode),
2613
+ onLeaveNotice: (remoteNodeId, sourceIsStreamEntryPoint) => {
2614
+ if (this.abortController.signal.aborted) {
2615
+ return;
2616
+ }
2617
+ const contact = this.options.nearbyNodeView.get(remoteNodeId)
2618
+ ?? this.options.randomNodeView.get(remoteNodeId)
2619
+ ?? this.options.neighbors.get(remoteNodeId)
2620
+ ?? this.options.proxyConnectionRpcLocal?.getConnection(remoteNodeId)?.remote;
2621
+ // TODO: check integrity of notifier?
2622
+ if (contact) {
2623
+ this.options.discoveryLayerNode.removeContact(remoteNodeId);
2624
+ this.options.neighbors.remove(remoteNodeId);
2625
+ this.options.nearbyNodeView.remove(remoteNodeId);
2626
+ this.options.randomNodeView.remove(remoteNodeId);
2627
+ this.options.leftNodeView.remove(remoteNodeId);
2628
+ this.options.rightNodeView.remove(remoteNodeId);
2629
+ this.options.neighborFinder.start([remoteNodeId]);
2630
+ this.options.proxyConnectionRpcLocal?.removeConnection(remoteNodeId);
2631
+ }
2632
+ if (sourceIsStreamEntryPoint) {
2633
+ this.emit('entryPointLeaveDetected');
2634
+ }
2635
+ },
2636
+ markForInspection: (remoteNodeId, messageId) => this.options.inspector.markMessage(remoteNodeId, messageId),
2637
+ plumtreeManager: this.options.plumtreeManager
2638
+ });
2639
+ }
2640
+ async start() {
2641
+ this.started = true;
2642
+ this.registerDefaultServerMethods();
2643
+ utils.addManagedEventListener(this.options.discoveryLayerNode, 'nearbyContactAdded', () => this.onNearbyContactAdded(), this.abortController.signal);
2644
+ utils.addManagedEventListener(this.options.discoveryLayerNode, 'nearbyContactRemoved', () => this.onNearbyContactRemoved(), this.abortController.signal);
2645
+ utils.addManagedEventListener(this.options.discoveryLayerNode, 'randomContactAdded', () => this.onRandomContactAdded(), this.abortController.signal);
2646
+ utils.addManagedEventListener(this.options.discoveryLayerNode, 'randomContactRemoved', () => this.onRandomContactRemoved(), this.abortController.signal);
2647
+ utils.addManagedEventListener(this.options.discoveryLayerNode, 'ringContactAdded', () => this.onRingContactsUpdated(), this.abortController.signal);
2648
+ utils.addManagedEventListener(this.options.discoveryLayerNode, 'ringContactRemoved', () => this.onRingContactsUpdated(), this.abortController.signal);
2649
+ utils.addManagedEventListener(this.options.transport, 'disconnected', (peerDescriptor) => this.onNodeDisconnected(peerDescriptor), this.abortController.signal);
2650
+ utils.addManagedEventListener(this.options.neighbors, 'nodeAdded', (id, remote) => {
2651
+ this.options.propagation.onNeighborJoined(id);
2652
+ this.options.connectionLocker.weakLockConnection(dht.toNodeId(remote.getPeerDescriptor()), this.options.streamPartId);
2653
+ this.emit('neighborConnected', id);
2654
+ }, this.abortController.signal);
2655
+ utils.addManagedEventListener(this.options.neighbors, 'nodeRemoved', (_id, remote) => {
2656
+ this.options.connectionLocker.weakUnlockConnection(dht.toNodeId(remote.getPeerDescriptor()), this.options.streamPartId);
2657
+ }, this.abortController.signal);
2658
+ if (this.options.proxyConnectionRpcLocal !== undefined) {
2659
+ utils.addManagedEventListener(this.options.proxyConnectionRpcLocal, 'newConnection', (id) => this.options.propagation.onNeighborJoined(id), this.abortController.signal);
2660
+ }
2661
+ if (this.options.plumtreeManager) {
2662
+ utils.addManagedEventListener(this.options.plumtreeManager, 'message', (msg) => this.emit('message', msg), this.abortController.signal);
2663
+ }
2664
+ this.options.neighborFinder.start();
2665
+ await this.options.neighborUpdateManager.start();
2666
+ }
2667
+ registerDefaultServerMethods() {
2668
+ this.options.rpcCommunicator.registerRpcNotification(StreamMessage, 'sendStreamMessage', (msg, context) => this.contentDeliveryRpcLocal.sendStreamMessage(msg, context));
2669
+ this.options.rpcCommunicator.registerRpcNotification(LeaveStreamPartNotice, 'leaveStreamPartNotice', (req, context) => this.contentDeliveryRpcLocal.leaveStreamPartNotice(req, context));
2670
+ this.options.rpcCommunicator.registerRpcMethod(TemporaryConnectionRequest, TemporaryConnectionResponse, 'openConnection', (req, context) => this.options.temporaryConnectionRpcLocal.openConnection(req, context));
2671
+ this.options.rpcCommunicator.registerRpcNotification(CloseTemporaryConnection, 'closeConnection', (req, context) => this.options.temporaryConnectionRpcLocal.closeConnection(req, context));
2672
+ }
2673
+ onRingContactsUpdated() {
2674
+ logger$8.trace('onRingContactsUpdated');
2675
+ if (this.isStopped()) {
2676
+ return;
2677
+ }
2678
+ const contacts = this.options.discoveryLayerNode.getRingContacts();
2679
+ this.options.leftNodeView.replaceAll(contacts.left.map((peer) => new ContentDeliveryRpcRemote(this.options.localPeerDescriptor, peer, this.options.rpcCommunicator, ContentDeliveryRpcClient, this.options.rpcRequestTimeout)));
2680
+ this.options.rightNodeView.replaceAll(contacts.right.map((peer) => new ContentDeliveryRpcRemote(this.options.localPeerDescriptor, peer, this.options.rpcCommunicator, ContentDeliveryRpcClient, this.options.rpcRequestTimeout)));
2681
+ }
2682
+ onNearbyContactAdded() {
2683
+ logger$8.trace(`New nearby contact found`);
2684
+ if (this.isStopped()) {
2685
+ return;
2686
+ }
2687
+ const closestContacts = this.options.discoveryLayerNode.getClosestContacts();
2688
+ this.updateNearbyNodeView(closestContacts);
2689
+ if (this.options.neighbors.size() < this.options.neighborTargetCount) {
2690
+ this.options.neighborFinder.start();
2691
+ }
2692
+ }
2693
+ onNearbyContactRemoved() {
2694
+ logger$8.trace(`Nearby contact removed`);
2695
+ if (this.isStopped()) {
2696
+ return;
2697
+ }
2698
+ const closestContacts = this.options.discoveryLayerNode.getClosestContacts();
2699
+ this.updateNearbyNodeView(closestContacts);
2700
+ }
2701
+ updateNearbyNodeView(nodes) {
2702
+ this.options.nearbyNodeView.replaceAll(Array.from(nodes).map((descriptor) => new ContentDeliveryRpcRemote(this.options.localPeerDescriptor, descriptor, this.options.rpcCommunicator, ContentDeliveryRpcClient, this.options.rpcRequestTimeout)));
2703
+ for (const descriptor of this.options.discoveryLayerNode.getNeighbors()) {
2704
+ if (this.options.nearbyNodeView.size() >= this.options.nodeViewSize) {
2705
+ break;
2706
+ }
2707
+ this.options.nearbyNodeView.add(new ContentDeliveryRpcRemote(this.options.localPeerDescriptor, descriptor, this.options.rpcCommunicator, ContentDeliveryRpcClient, this.options.rpcRequestTimeout));
2708
+ }
2709
+ }
2710
+ onRandomContactAdded() {
2711
+ if (this.isStopped()) {
2712
+ return;
2713
+ }
2714
+ const randomContacts = this.options.discoveryLayerNode.getRandomContacts(this.options.nodeViewSize);
2715
+ this.options.randomNodeView.replaceAll(randomContacts.map((descriptor) => new ContentDeliveryRpcRemote(this.options.localPeerDescriptor, descriptor, this.options.rpcCommunicator, ContentDeliveryRpcClient, this.options.rpcRequestTimeout)));
2716
+ if (this.options.neighbors.size() < this.options.neighborTargetCount) {
2717
+ this.options.neighborFinder.start();
2718
+ }
2719
+ }
2720
+ onRandomContactRemoved() {
2721
+ logger$8.trace(`New random contact removed`);
2722
+ if (this.isStopped()) {
2723
+ return;
2724
+ }
2725
+ const randomContacts = this.options.discoveryLayerNode.getRandomContacts(this.options.nodeViewSize);
2726
+ this.options.randomNodeView.replaceAll(randomContacts.map((descriptor) => new ContentDeliveryRpcRemote(this.options.localPeerDescriptor, descriptor, this.options.rpcCommunicator, ContentDeliveryRpcClient, this.options.rpcRequestTimeout)));
2727
+ }
2728
+ onNodeDisconnected(peerDescriptor) {
2729
+ const nodeId = dht.toNodeId(peerDescriptor);
2730
+ if (this.options.neighbors.has(nodeId)) {
2731
+ this.options.neighbors.remove(nodeId);
2732
+ this.options.neighborFinder.start([nodeId]);
2733
+ this.options.temporaryConnectionRpcLocal.removeNode(nodeId);
2734
+ }
2735
+ }
2736
+ hasProxyConnection(nodeId) {
2737
+ if (this.options.proxyConnectionRpcLocal) {
2738
+ return this.options.proxyConnectionRpcLocal.hasConnection(nodeId);
2739
+ }
2740
+ return false;
2741
+ }
2742
+ stop() {
2743
+ if (!this.started) {
2744
+ return;
2745
+ }
2746
+ this.abortController.abort();
2747
+ this.options.proxyConnectionRpcLocal?.stop();
2748
+ this.options.neighbors.getAll().map((remote) => {
2749
+ remote.leaveStreamPartNotice(this.options.streamPartId, this.options.isLocalNodeEntryPoint());
2750
+ this.options.connectionLocker.weakUnlockConnection(dht.toNodeId(remote.getPeerDescriptor()), this.options.streamPartId);
2751
+ });
2752
+ this.options.rpcCommunicator.destroy();
2753
+ this.removeAllListeners();
2754
+ this.options.plumtreeManager?.stop();
2755
+ this.options.nearbyNodeView.stop();
2756
+ this.options.neighbors.stop();
2757
+ this.options.randomNodeView.stop();
2758
+ this.options.neighborFinder.stop();
2759
+ this.options.neighborUpdateManager.stop();
2760
+ this.options.inspector.stop();
2761
+ }
2762
+ broadcast(msg, previousNode) {
2763
+ if (!previousNode) {
2764
+ markAndCheckDuplicate(this.duplicateDetectors, msg.messageId, msg.previousMessageRef);
2765
+ }
2766
+ this.emit('message', msg);
2767
+ const skipBackPropagation = previousNode !== undefined && !this.options.temporaryConnectionRpcLocal.hasNode(previousNode);
2768
+ this.options.propagation.feedUnseenMessage(msg, this.getPropagationTargets(msg), skipBackPropagation ? previousNode : null);
2769
+ this.messagesPropagated += 1;
2770
+ }
2771
+ inspect(peerDescriptor) {
2772
+ return this.options.inspector.inspect(peerDescriptor);
2773
+ }
2774
+ getPropagationTargets(msg) {
2775
+ let propagationTargets = this.options.neighbors.getIds();
2776
+ if (this.options.proxyConnectionRpcLocal) {
2777
+ propagationTargets = propagationTargets.concat(this.options.proxyConnectionRpcLocal.getPropagationTargets(msg));
2778
+ }
2779
+ propagationTargets = propagationTargets.concat(this.options.temporaryConnectionRpcLocal.getNodes().getIds());
2780
+ return propagationTargets;
2781
+ }
2782
+ getOwnNodeId() {
2783
+ return dht.toNodeId(this.options.localPeerDescriptor);
2784
+ }
2785
+ getOutgoingHandshakeCount() {
2786
+ return this.options.handshaker.getOngoingHandshakes().size;
2787
+ }
2788
+ getNeighbors() {
2789
+ if (!this.started && this.isStopped()) {
2790
+ return [];
2791
+ }
2792
+ return this.options.neighbors.getAll().map((n) => n.getPeerDescriptor());
2793
+ }
2794
+ getInfos() {
2795
+ return this.options.neighbors.getAll().map((n) => {
2796
+ return {
2797
+ peerDescriptor: n.getPeerDescriptor(),
2798
+ rtt: n.getRtt()
2799
+ };
2800
+ });
2801
+ }
2802
+ getNearbyNodeView() {
2803
+ return this.options.nearbyNodeView;
2804
+ }
2805
+ getDiagnosticInfo() {
2806
+ return {
2807
+ neighborCount: this.options.neighbors.size(),
2808
+ nearbyNodeViewCount: this.options.nearbyNodeView.size(),
2809
+ randomNodeViewCount: this.options.randomNodeView.size(),
2810
+ leftNodeViewCount: this.options.leftNodeView.size(),
2811
+ rightNodeViewCount: this.options.rightNodeView.size(),
2812
+ messagesPropagated: this.messagesPropagated
2813
+ };
2814
+ }
2815
+ isStopped() {
2816
+ return this.abortController.signal.aborted;
2817
+ }
2818
+ }
2819
+
2820
+ const getValuesOfIncludedKeys = (nodes, exclude, wsOnly = false) => {
2821
+ const values = wsOnly
2822
+ ? Array.from(nodes.entries()).filter(([_, node]) => node.getPeerDescriptor().websocket !== undefined)
2823
+ : Array.from(nodes.entries());
2824
+ return values
2825
+ .filter(([id]) => !exclude.includes(id))
2826
+ .map(([_id, node]) => node);
2827
+ };
2828
+ // The items in the list are in the insertion order
2829
+ class NodeList extends eventemitter3.EventEmitter {
2830
+ nodes;
2831
+ limit;
2832
+ ownId;
2833
+ constructor(ownId, limit) {
2834
+ super();
2835
+ this.nodes = new Map();
2836
+ this.limit = limit;
2837
+ this.ownId = ownId;
2838
+ }
2839
+ add(remote) {
2840
+ const nodeId = dht.toNodeId(remote.getPeerDescriptor());
2841
+ if ((this.ownId !== nodeId) && (this.nodes.size < this.limit)) {
2842
+ const isExistingNode = this.nodes.has(nodeId);
2843
+ this.nodes.set(nodeId, remote);
2844
+ if (!isExistingNode) {
2845
+ this.emit('nodeAdded', nodeId, remote);
2846
+ }
2847
+ }
2848
+ }
2849
+ remove(nodeId) {
2850
+ if (this.nodes.has(nodeId)) {
2851
+ const remote = this.nodes.get(nodeId);
2852
+ this.nodes.delete(nodeId);
2853
+ this.emit('nodeRemoved', nodeId, remote);
2854
+ }
2855
+ }
2856
+ has(nodeId) {
2857
+ return this.nodes.has(nodeId);
2858
+ }
2859
+ // Replace nodes does not emit nodeRemoved events, use with caution
2860
+ replaceAll(neighbors) {
2861
+ this.nodes.clear();
2862
+ const limited = neighbors.splice(0, this.limit);
2863
+ limited.forEach((remote) => {
2864
+ this.add(remote);
2865
+ });
2866
+ }
2867
+ getIds() {
2868
+ return Array.from(this.nodes.keys());
2869
+ }
2870
+ get(id) {
2871
+ return this.nodes.get(id);
2872
+ }
2873
+ size(exclude = []) {
2874
+ return Array.from(this.nodes.keys()).filter((node) => !exclude.includes(node)).length;
2875
+ }
2876
+ getRandom(exclude) {
2877
+ return sample(getValuesOfIncludedKeys(this.nodes, exclude));
2878
+ }
2879
+ getFirst(exclude, wsOnly = false) {
2880
+ const included = getValuesOfIncludedKeys(this.nodes, exclude, wsOnly);
2881
+ return included[0];
2882
+ }
2883
+ getFirstAndLast(exclude) {
2884
+ const included = getValuesOfIncludedKeys(this.nodes, exclude);
2885
+ if (included.length === 0) {
2886
+ return [];
2887
+ }
2888
+ return included.length > 1 ? [this.getFirst(exclude), this.getLast(exclude)] : [this.getFirst(exclude)];
2889
+ }
2890
+ getLast(exclude) {
2891
+ const included = getValuesOfIncludedKeys(this.nodes, exclude);
2892
+ return included[included.length - 1];
2893
+ }
2894
+ getAll() {
2895
+ return Array.from(this.nodes.values());
2896
+ }
2897
+ stop() {
2898
+ this.nodes.forEach((node) => this.remove(dht.toNodeId(node.getPeerDescriptor())));
2899
+ this.removeAllListeners();
2900
+ }
2901
+ }
2902
+
2903
+ /**
2904
+ * A "Map" implementation with a maximum size and TTL expiration on entries.
2905
+ *
2906
+ * When full, room is made for new entries by dropping existing by FIFO method.
2907
+ *
2908
+ * Entries have a TTL after which they are considered stale. Stale items are
2909
+ * not returned when querying.
2910
+ *
2911
+ */
2912
+ class FifoMapWithTTL {
2913
+ // class invariant: the keys present in `items` and `dropQueue` are the same set.
2914
+ items = new Map();
2915
+ dropQueue = yallist.Yallist.create(); // queue is used to determine deletion order when full
2916
+ ttlInMs;
2917
+ maxSize;
2918
+ onItemDropped;
2919
+ timeProvider;
2920
+ constructor({ ttlInMs, maxSize, onItemDropped = () => { }, timeProvider = Date.now }) {
2921
+ if (ttlInMs < 0) {
2922
+ throw new Error(`ttlInMs (${ttlInMs}) cannot be < 0`);
2923
+ }
2924
+ if (maxSize < 0) {
2925
+ throw new Error(`maxSize (${maxSize}) cannot be < 0`);
2926
+ }
2927
+ this.ttlInMs = ttlInMs;
2928
+ this.maxSize = maxSize;
2929
+ this.onItemDropped = onItemDropped;
2930
+ this.timeProvider = timeProvider;
2931
+ }
2932
+ set(key, value) {
2933
+ if (this.maxSize === 0) {
2934
+ return;
2935
+ }
2936
+ if (this.items.size > this.maxSize) {
2937
+ throw new Error('assertion error: maximum size exceeded');
2938
+ }
2939
+ // delete an existing entry if exists
2940
+ this.delete(key);
2941
+ // make room for new entry
2942
+ if (this.items.size === this.maxSize) {
2943
+ const keyToDel = this.dropQueue.shift();
2944
+ if (keyToDel === undefined) {
2945
+ throw new Error('assertion error: queue empty but still have items');
2946
+ }
2947
+ this.items.delete(keyToDel);
2948
+ this.onItemDropped(keyToDel);
2949
+ }
2950
+ // add entry
2951
+ const dropQueueNode = new yallist.Node(key);
2952
+ this.dropQueue.pushNode(dropQueueNode);
2953
+ this.items.set(key, {
2954
+ value,
2955
+ dropQueueNode,
2956
+ expiresAt: this.timeProvider() + this.ttlInMs
2957
+ });
2958
+ }
2959
+ delete(key) {
2960
+ const item = this.items.get(key);
2961
+ if (item !== undefined) {
2962
+ this.items.delete(key);
2963
+ this.dropQueue.removeNode(item.dropQueueNode);
2964
+ this.onItemDropped(key);
2965
+ }
2966
+ }
2967
+ get(key) {
2968
+ const item = this.items.get(key);
2969
+ if (item === undefined) {
2970
+ return undefined;
2971
+ }
2972
+ if (item.expiresAt <= this.timeProvider()) {
2973
+ this.delete(key);
2974
+ return undefined;
2975
+ }
2976
+ return item.value;
2977
+ }
2978
+ values() {
2979
+ const keys = [...this.items.keys()];
2980
+ const values = [];
2981
+ for (const key of keys) {
2982
+ const value = this.get(key);
2983
+ if (value !== undefined) {
2984
+ values.push(value);
2985
+ }
2986
+ }
2987
+ return values;
2988
+ }
2989
+ }
2990
+
2991
+ /**
2992
+ * Keeps track of propagation tasks for the needs of message propagation logic.
2993
+ *
2994
+ * Properties:
2995
+ * - Allows fetching propagation tasks by StreamPartID
2996
+ * - Upper bound on number of tasks stored, replacement policy if FIFO
2997
+ * - Items have a TTL, after which they are considered stale and not returned when querying
2998
+ **/
2999
+ class PropagationTaskStore {
3000
+ tasks;
3001
+ constructor(ttlInMs, maxTasks) {
3002
+ this.tasks = new FifoMapWithTTL({
3003
+ ttlInMs,
3004
+ maxSize: maxTasks
3005
+ });
3006
+ }
3007
+ get() {
3008
+ return this.tasks.values();
3009
+ }
3010
+ add(task) {
3011
+ const messageId = task.message.messageId;
3012
+ this.tasks.set(messageId, task);
3013
+ }
3014
+ delete(messageId) {
3015
+ this.tasks.delete(messageId); // causes `onKeyDropped` to be invoked
3016
+ }
3017
+ }
3018
+
3019
+ const DEFAULT_PROPAGATION_BUFFER_TTL = 10 * 1000;
3020
+ const DEFAULT_MIN_PROPAGATION_TARGETS = 2;
3021
+ const DEFAULT_MAX_PROPAGATION_BUFFER_SIZE = 150;
3022
+ /**
3023
+ * Message propagation logic of a node. Given a message, this class will actively attempt to propagate it to
3024
+ * `minPropagationTargets` neighbors until success or TTL expiration.
3025
+ *
3026
+ * Setting `minPropagationTargets = 0` effectively disables any propagation reattempts. A message will then
3027
+ * only be propagated exactly once, to neighbors that are present at that moment, in a fire-and-forget manner.
3028
+ */
3029
+ class Propagation {
3030
+ sendToNeighbor;
3031
+ minPropagationTargets;
3032
+ activeTaskStore;
3033
+ constructor({ sendToNeighbor, minPropagationTargets, maxMessages, ttl }) {
3034
+ this.sendToNeighbor = sendToNeighbor;
3035
+ this.minPropagationTargets = minPropagationTargets;
3036
+ this.activeTaskStore = new PropagationTaskStore(ttl, maxMessages);
3037
+ }
3038
+ /**
3039
+ * Node should invoke this when it learns about a new message
3040
+ */
3041
+ feedUnseenMessage(message, targets, source) {
3042
+ const task = {
3043
+ message,
3044
+ source,
3045
+ handledNeighbors: new Set()
3046
+ };
3047
+ this.activeTaskStore.add(task);
3048
+ for (const target of targets) {
3049
+ this.sendAndAwaitThenMark(task, target);
3050
+ }
3051
+ }
3052
+ /**
3053
+ * Node should invoke this when it learns about a new node stream assignment
3054
+ */
3055
+ onNeighborJoined(neighborId) {
3056
+ const tasks = this.activeTaskStore.get();
3057
+ for (const task of tasks) {
3058
+ this.sendAndAwaitThenMark(task, neighborId);
3059
+ }
3060
+ }
3061
+ sendAndAwaitThenMark({ message, source, handledNeighbors }, neighborId) {
3062
+ if (!handledNeighbors.has(neighborId) && neighborId !== source) {
3063
+ (async () => {
3064
+ try {
3065
+ await this.sendToNeighbor(neighborId, message);
3066
+ }
3067
+ catch {
3068
+ return;
3069
+ }
3070
+ // Side-note: due to asynchronicity, the task being modified at this point could already be stale and
3071
+ // deleted from `activeTaskStore`. However, as modifying it or re-deleting it is pretty much
3072
+ // inconsequential at this point, leaving the logic as is.
3073
+ handledNeighbors.add(neighborId);
3074
+ if (handledNeighbors.size >= this.minPropagationTargets) {
3075
+ this.activeTaskStore.delete(message.messageId);
3076
+ }
3077
+ })();
3078
+ }
3079
+ }
3080
+ }
3081
+
3082
+ const logger$7 = new utils.Logger('ProxyConnectionRpcLocal');
3083
+ class ProxyConnectionRpcLocal extends eventemitter3.EventEmitter {
3084
+ options;
3085
+ connections = new Map();
3086
+ constructor(options) {
3087
+ super();
3088
+ this.options = options;
3089
+ this.options.rpcCommunicator.registerRpcMethod(ProxyConnectionRequest, ProxyConnectionResponse, 'requestConnection', (msg, context) => this.requestConnection(msg, context));
3090
+ }
3091
+ getConnection(nodeId) {
3092
+ return this.connections.get(nodeId);
3093
+ }
3094
+ hasConnection(nodeId) {
3095
+ return this.connections.has(nodeId);
3096
+ }
3097
+ removeConnection(nodeId) {
3098
+ this.connections.delete(nodeId);
3099
+ }
3100
+ stop() {
3101
+ this.connections.forEach((connection) => connection.remote.leaveStreamPartNotice(this.options.streamPartId, false));
3102
+ this.connections.clear();
3103
+ this.removeAllListeners();
3104
+ }
3105
+ getPropagationTargets(msg) {
3106
+ if (msg.body.oneofKind === 'groupKeyRequest') {
3107
+ try {
3108
+ const recipientId = msg.body.groupKeyRequest.recipientId;
3109
+ return this.getNodeIdsForUserId(utils.toUserId(recipientId));
3110
+ }
3111
+ catch (err) {
3112
+ logger$7.trace(`Could not parse GroupKeyRequest`, { err });
3113
+ return [];
3114
+ }
3115
+ }
3116
+ else {
3117
+ return this.getSubscribers();
3118
+ }
3119
+ }
3120
+ getNodeIdsForUserId(userId) {
3121
+ return Array.from(this.connections.keys()).filter((nodeId) => this.connections.get(nodeId).userId === userId);
3122
+ }
3123
+ getSubscribers() {
3124
+ return Array.from(this.connections.keys()).filter((key) => {
3125
+ const direction = this.connections.get(key).direction;
3126
+ return direction === undefined || direction === exports.ProxyDirection.SUBSCRIBE;
3127
+ });
3128
+ }
3129
+ // IProxyConnectionRpc server method
3130
+ async requestConnection(request, context) {
3131
+ const senderPeerDescriptor = context.incomingSourceDescriptor;
3132
+ const remoteNodeId = dht.toNodeId(senderPeerDescriptor);
3133
+ this.connections.set(remoteNodeId, {
3134
+ direction: request.direction,
3135
+ userId: utils.toUserId(request.userId),
3136
+ remote: new ContentDeliveryRpcRemote(this.options.localPeerDescriptor, senderPeerDescriptor, this.options.rpcCommunicator, ContentDeliveryRpcClient)
3137
+ });
3138
+ const response = {
3139
+ accepted: true
3140
+ };
3141
+ logger$7.trace(`Accepted connection request from ${remoteNodeId} to ${this.options.streamPartId}`);
3142
+ this.emit('newConnection', remoteNodeId);
3143
+ return response;
3144
+ }
3145
+ }
3146
+
3147
+ const logger$6 = new utils.Logger('TemporaryConnectionRpcRemote');
3148
+ class TemporaryConnectionRpcRemote extends dht.RpcRemote {
3149
+ async openConnection() {
3150
+ try {
3151
+ const response = await this.getClient().openConnection({}, this.formDhtRpcOptions());
3152
+ return response.accepted;
3153
+ }
3154
+ catch (err) {
3155
+ logger$6.debug(`temporaryConnection to ${dht.toNodeId(this.getPeerDescriptor())} failed`, { err });
3156
+ return false;
3157
+ }
3158
+ }
3159
+ async closeConnection() {
3160
+ try {
3161
+ await this.getClient().closeConnection({}, this.formDhtRpcOptions({
3162
+ connect: false,
3163
+ notification: true
3164
+ }));
3165
+ }
3166
+ catch (err) {
3167
+ logger$6.trace(`closeConnection to ${dht.toNodeId(this.getPeerDescriptor())} failed`, { err });
3168
+ }
3169
+ }
3170
+ }
3171
+
3172
+ const createMessageKey = (messageId) => {
3173
+ return `${utils.toUserId(messageId.publisherId)}:${messageId.messageChainId}:${messageId.timestamp}:${messageId.sequenceNumber}`;
3174
+ };
3175
+ class InspectSession extends eventemitter3.EventEmitter {
3176
+ // Boolean indicates if the message has been received by the inspected node
3177
+ inspectionMessages = new Map();
3178
+ inspectedNode;
3179
+ constructor(options) {
3180
+ super();
3181
+ this.inspectedNode = options.inspectedNode;
3182
+ }
3183
+ markMessage(remoteNodeId, messageId) {
3184
+ const messageKey = createMessageKey(messageId);
3185
+ if (!this.inspectionMessages.has(messageKey)) {
3186
+ this.inspectionMessages.set(messageKey, remoteNodeId === this.inspectedNode);
3187
+ }
3188
+ else if (this.inspectionMessages.has(messageKey)
3189
+ && this.inspectionMessages.get(messageKey) === false
3190
+ && remoteNodeId === this.inspectedNode) {
3191
+ this.emit('done');
3192
+ }
3193
+ else if (this.inspectionMessages.has(messageKey)
3194
+ && this.inspectionMessages.get(messageKey) === true) {
3195
+ this.emit('done');
3196
+ }
3197
+ }
3198
+ getInspectedMessageCount() {
3199
+ return this.inspectionMessages.size;
3200
+ }
3201
+ onlyMarkedByInspectedNode() {
3202
+ return Array.from(this.inspectionMessages.values()).every((value) => value === true);
3203
+ }
3204
+ stop() {
3205
+ this.emit('done');
3206
+ }
3207
+ }
3208
+
3209
+ const logger$5 = new utils.Logger('Inspector');
3210
+ const DEFAULT_TIMEOUT = 60 * 1000;
3211
+ class Inspector {
3212
+ sessions = new Map();
3213
+ streamPartId;
3214
+ localPeerDescriptor;
3215
+ rpcCommunicator;
3216
+ connectionLocker;
3217
+ inspectionTimeout;
3218
+ openInspectConnection;
3219
+ closeInspectConnection;
3220
+ constructor(options) {
3221
+ this.streamPartId = options.streamPartId;
3222
+ this.localPeerDescriptor = options.localPeerDescriptor;
3223
+ this.rpcCommunicator = options.rpcCommunicator;
3224
+ this.connectionLocker = options.connectionLocker;
3225
+ this.inspectionTimeout = options.inspectionTimeout ?? DEFAULT_TIMEOUT;
3226
+ this.openInspectConnection = options.openInspectConnection ?? this.defaultOpenInspectConnection;
3227
+ this.closeInspectConnection = options.closeInspectConnection ?? this.defaultCloseInspectConnection;
3228
+ }
3229
+ async defaultOpenInspectConnection(peerDescriptor, lockId) {
3230
+ const rpcRemote = new TemporaryConnectionRpcRemote(this.localPeerDescriptor, peerDescriptor, this.rpcCommunicator, TemporaryConnectionRpcClient);
3231
+ await rpcRemote.openConnection();
3232
+ this.connectionLocker.weakLockConnection(dht.toNodeId(peerDescriptor), lockId);
3233
+ }
3234
+ async defaultCloseInspectConnection(peerDescriptor, lockId) {
3235
+ const rpcRemote = new TemporaryConnectionRpcRemote(this.localPeerDescriptor, peerDescriptor, this.rpcCommunicator, TemporaryConnectionRpcClient);
3236
+ await rpcRemote.closeConnection();
3237
+ this.connectionLocker.weakUnlockConnection(dht.toNodeId(peerDescriptor), lockId);
3238
+ }
3239
+ async inspect(peerDescriptor) {
3240
+ const nodeId = dht.toNodeId(peerDescriptor);
3241
+ const session = new InspectSession({
3242
+ inspectedNode: nodeId
3243
+ });
3244
+ const lockId = `inspector-${this.streamPartId}`;
3245
+ this.sessions.set(nodeId, session);
3246
+ await this.openInspectConnection(peerDescriptor, lockId);
3247
+ let success = false;
3248
+ try {
3249
+ await utils.waitForEvent(session, 'done', this.inspectionTimeout);
3250
+ success = true;
3251
+ }
3252
+ catch {
3253
+ logger$5.trace('Inspect session timed out, removing');
3254
+ }
3255
+ finally {
3256
+ await this.closeInspectConnection(peerDescriptor, lockId);
3257
+ this.sessions.delete(nodeId);
3258
+ }
3259
+ return success || session.getInspectedMessageCount() < 1 || session.onlyMarkedByInspectedNode();
3260
+ }
3261
+ markMessage(sender, messageId) {
3262
+ this.sessions.forEach((session) => session.markMessage(sender, messageId));
3263
+ }
3264
+ isInspected(nodeId) {
3265
+ return this.sessions.has(nodeId);
3266
+ }
3267
+ stop() {
3268
+ this.sessions.forEach((session) => {
3269
+ session.stop();
3270
+ });
3271
+ this.sessions.clear();
3272
+ }
3273
+ }
3274
+
3275
+ const LOCK_ID_BASE = 'system/content-delivery/temporary-connection/';
3276
+ class TemporaryConnectionRpcLocal {
3277
+ options;
3278
+ temporaryNodes;
3279
+ lockId;
3280
+ constructor(options) {
3281
+ this.options = options;
3282
+ // TODO use options option or named constant?
3283
+ this.temporaryNodes = new NodeList(dht.toNodeId(options.localPeerDescriptor), 10);
3284
+ this.lockId = LOCK_ID_BASE + options.streamPartId;
3285
+ }
3286
+ getNodes() {
3287
+ return this.temporaryNodes;
3288
+ }
3289
+ hasNode(node) {
3290
+ return this.temporaryNodes.has(node);
3291
+ }
3292
+ removeNode(nodeId) {
3293
+ this.temporaryNodes.remove(nodeId);
3294
+ this.options.connectionLocker.weakUnlockConnection(nodeId, this.lockId);
3295
+ }
3296
+ async openConnection(_request, context) {
3297
+ const sender = context.incomingSourceDescriptor;
3298
+ const remote = new ContentDeliveryRpcRemote(this.options.localPeerDescriptor, sender, this.options.rpcCommunicator, ContentDeliveryRpcClient);
3299
+ this.temporaryNodes.add(remote);
3300
+ this.options.connectionLocker.weakLockConnection(dht.toNodeId(sender), this.lockId);
3301
+ return {
3302
+ accepted: true
3303
+ };
3304
+ }
3305
+ async closeConnection(_request, context) {
3306
+ const remoteNodeId = dht.toNodeId(context.incomingSourceDescriptor);
3307
+ this.removeNode(remoteNodeId);
3308
+ return {};
3309
+ }
3310
+ }
3311
+
3312
+ const formStreamPartContentDeliveryServiceId = (streamPartId) => {
3313
+ // could be "content-delivery" instead of "delivery", but that is a breaking change
3314
+ return `stream-part-delivery-${streamPartId}`;
3315
+ };
3316
+
3317
+ class PlumtreeRpcLocal {
3318
+ neighbors;
3319
+ pausedNodes;
3320
+ onMetadataCb;
3321
+ sendBuffer;
3322
+ constructor(neighbors, pausedNodes, onMetaDataCb, sendBuffer) {
3323
+ this.neighbors = neighbors;
3324
+ this.pausedNodes = pausedNodes;
3325
+ this.onMetadataCb = onMetaDataCb;
3326
+ this.sendBuffer = sendBuffer;
3327
+ }
3328
+ async sendMetadata(message, context) {
3329
+ const previousNode = context.incomingSourceDescriptor;
3330
+ await this.onMetadataCb(message, previousNode);
3331
+ return Empty;
3332
+ }
3333
+ async pauseNeighbor(request, context) {
3334
+ const sender = dht.toNodeId(context.incomingSourceDescriptor);
3335
+ if (this.neighbors.has(sender)) {
3336
+ this.pausedNodes.add(sender, request.messageChainId);
3337
+ }
3338
+ return Empty;
3339
+ }
3340
+ async resumeNeighbor(request, context) {
3341
+ const sender = context.incomingSourceDescriptor;
3342
+ this.pausedNodes.delete(dht.toNodeId(sender), request.messageChainId);
3343
+ await this.sendBuffer(request.fromTimestamp, request.messageChainId, sender);
3344
+ return Empty;
3345
+ }
3346
+ }
3347
+
3348
+ class PlumtreeRpcRemote extends dht.RpcRemote {
3349
+ async sendMetadata(msg) {
3350
+ const options = this.formDhtRpcOptions({
3351
+ notification: true
3352
+ });
3353
+ await this.getClient().sendMetadata(msg, options);
3354
+ }
3355
+ async pauseNeighbor(messageChainId) {
3356
+ const options = this.formDhtRpcOptions({
3357
+ notification: true
3358
+ });
3359
+ await this.getClient().pauseNeighbor({ messageChainId }, options);
3360
+ }
3361
+ async resumeNeighbor(fromTimestamp, messageChainId) {
3362
+ const options = this.formDhtRpcOptions({
3363
+ notification: true
3364
+ });
3365
+ await this.getClient().resumeNeighbor({ fromTimestamp, messageChainId }, options);
3366
+ }
3367
+ }
3368
+
3369
+ class PausedNeighbors {
3370
+ pausedNeighbors;
3371
+ limit;
3372
+ constructor(limit) {
3373
+ this.pausedNeighbors = new Map();
3374
+ this.limit = limit;
3375
+ }
3376
+ add(node, msgChainId) {
3377
+ if (!this.pausedNeighbors.has(msgChainId)) {
3378
+ this.pausedNeighbors.set(msgChainId, new Set());
3379
+ }
3380
+ if (this.pausedNeighbors.get(msgChainId).size >= this.limit) {
3381
+ return;
3382
+ }
3383
+ this.pausedNeighbors.get(msgChainId).add(node);
3384
+ }
3385
+ delete(node, msgChainId) {
3386
+ this.pausedNeighbors.get(msgChainId)?.delete(node);
3387
+ if (this.pausedNeighbors.get(msgChainId)?.size === 0) {
3388
+ this.pausedNeighbors.delete(msgChainId);
3389
+ }
3390
+ }
3391
+ deleteAll(node) {
3392
+ this.pausedNeighbors.forEach((neighbors, msgChainId) => {
3393
+ neighbors.delete(node);
3394
+ if (neighbors.size === 0) {
3395
+ this.pausedNeighbors.delete(msgChainId);
3396
+ }
3397
+ });
3398
+ }
3399
+ isPaused(node, msgChainId) {
3400
+ if (!this.pausedNeighbors.has(msgChainId)) {
3401
+ return false;
3402
+ }
3403
+ return this.pausedNeighbors.get(msgChainId).has(node);
3404
+ }
3405
+ forEach(fn) {
3406
+ this.pausedNeighbors.forEach((neighbors, msgChainId) => {
3407
+ fn(neighbors, msgChainId);
3408
+ });
3409
+ }
3410
+ size(msgChainId) {
3411
+ return this.pausedNeighbors.get(msgChainId)?.size ?? 0;
3412
+ }
3413
+ }
3414
+
3415
+ const MAX_PAUSED_NEIGHBORS_DEFAULT = 3;
3416
+ const logger$4 = new utils.Logger('PlumtreeManager');
3417
+ class PlumtreeManager extends eventemitter3.EventEmitter {
3418
+ neighbors;
3419
+ localPeerDescriptor;
3420
+ // We have paused sending real data to these neighbrs and only send metadata
3421
+ localPausedNeighbors;
3422
+ // We have asked these nodes to pause sending real data to us, used to limit sending of pausing and resuming requests
3423
+ remotePausedNeighbors;
3424
+ rpcLocal;
3425
+ latestMessages = new Map();
3426
+ rpcCommunicator;
3427
+ metadataTimestampsAheadOfRealData = new Map();
3428
+ maxPausedNeighbors;
3429
+ constructor(options) {
3430
+ super();
3431
+ this.neighbors = options.neighbors;
3432
+ this.maxPausedNeighbors = options.maxPausedNeighbors ?? MAX_PAUSED_NEIGHBORS_DEFAULT;
3433
+ this.localPeerDescriptor = options.localPeerDescriptor;
3434
+ this.localPausedNeighbors = new PausedNeighbors(options.maxPausedNeighbors ?? MAX_PAUSED_NEIGHBORS_DEFAULT);
3435
+ this.remotePausedNeighbors = new PausedNeighbors(options.maxPausedNeighbors ?? MAX_PAUSED_NEIGHBORS_DEFAULT);
3436
+ this.rpcLocal = new PlumtreeRpcLocal(this.neighbors, this.localPausedNeighbors, (metadata, previousNode) => this.onMetadata(metadata, previousNode), (fromTimestamp, msgChainId, remotePeerDescriptor) => this.sendBuffer(fromTimestamp, msgChainId, remotePeerDescriptor));
3437
+ this.neighbors.on('nodeRemoved', (nodeId) => this.onNeighborRemoved(nodeId));
3438
+ this.rpcCommunicator = options.rpcCommunicator;
3439
+ this.rpcCommunicator.registerRpcNotification(MessageID, 'sendMetadata', (msg, context) => this.rpcLocal.sendMetadata(msg, context));
3440
+ this.rpcCommunicator.registerRpcNotification(PauseNeighborRequest, 'pauseNeighbor', (msg, context) => this.rpcLocal.pauseNeighbor(msg, context));
3441
+ this.rpcCommunicator.registerRpcNotification(ResumeNeighborRequest, 'resumeNeighbor', (msg, context) => this.rpcLocal.resumeNeighbor(msg, context));
3442
+ }
3443
+ async pauseNeighbor(node, msgChainId) {
3444
+ if (this.neighbors.has(dht.toNodeId(node))
3445
+ && !this.remotePausedNeighbors.isPaused(dht.toNodeId(node), msgChainId)
3446
+ && this.remotePausedNeighbors.size(msgChainId) < this.maxPausedNeighbors) {
3447
+ logger$4.debug(`Pausing neighbor ${dht.toNodeId(node)}`);
3448
+ this.remotePausedNeighbors.add(dht.toNodeId(node), msgChainId);
3449
+ const remote = this.createRemote(node);
3450
+ await remote.pauseNeighbor(msgChainId);
3451
+ }
3452
+ }
3453
+ async resumeNeighbor(node, msgChainId, fromTimestamp) {
3454
+ if (this.remotePausedNeighbors.isPaused(dht.toNodeId(node), msgChainId)) {
3455
+ logger$4.debug(`Resuming neighbor ${dht.toNodeId(node)}`);
3456
+ this.remotePausedNeighbors.delete(dht.toNodeId(node), msgChainId);
3457
+ const remote = this.createRemote(node);
3458
+ await remote.resumeNeighbor(fromTimestamp, msgChainId);
3459
+ }
3460
+ }
3461
+ onNeighborRemoved(nodeId) {
3462
+ this.localPausedNeighbors.deleteAll(nodeId);
3463
+ this.remotePausedNeighbors.deleteAll(nodeId);
3464
+ if (this.neighbors.size() > 0) {
3465
+ this.remotePausedNeighbors.forEach((pausedNeighbors, msgChainId) => {
3466
+ if (pausedNeighbors.size >= this.neighbors.size()) {
3467
+ logger$4.debug('All neighbors are paused, resuming first neighbor');
3468
+ const neighborToResume = this.neighbors.getFirst([]).getPeerDescriptor();
3469
+ setImmediate(() => this.resumeNeighbor(neighborToResume, msgChainId, this.getLatestMessageTimestamp(msgChainId)));
3470
+ }
3471
+ });
3472
+ }
3473
+ }
3474
+ getLatestMessageTimestamp(msgChainId) {
3475
+ if (!this.latestMessages.has(msgChainId) || this.latestMessages.get(msgChainId).length === 0) {
3476
+ return 0;
3477
+ }
3478
+ return this.latestMessages.get(msgChainId)[this.latestMessages.get(msgChainId).length - 1].messageId.timestamp;
3479
+ }
3480
+ async sendBuffer(fromTimestamp, msgChainId, neighbor) {
3481
+ const remote = new ContentDeliveryRpcRemote(this.localPeerDescriptor, neighbor, this.rpcCommunicator, ContentDeliveryRpcClient);
3482
+ const messages = this.latestMessages.get(msgChainId)?.filter((msg) => msg.messageId.timestamp > fromTimestamp) ?? [];
3483
+ await Promise.all(messages.map((msg) => remote.sendStreamMessage(msg)));
3484
+ }
3485
+ async onMetadata(msg, previousNode) {
3486
+ // If we receive newer metadata than messages in the buffer, resume the sending neighbor
3487
+ const latestMessageTimestamp = this.getLatestMessageTimestamp(msg.messageChainId);
3488
+ if (latestMessageTimestamp < msg.timestamp) {
3489
+ if (!this.metadataTimestampsAheadOfRealData.has(msg.messageChainId)) {
3490
+ this.metadataTimestampsAheadOfRealData.set(msg.messageChainId, new Set());
3491
+ }
3492
+ this.metadataTimestampsAheadOfRealData.get(msg.messageChainId).add(msg.timestamp);
3493
+ if (this.metadataTimestampsAheadOfRealData.get(msg.messageChainId).size > 1) {
3494
+ await this.resumeNeighbor(previousNode, msg.messageChainId, this.getLatestMessageTimestamp(msg.messageChainId));
3495
+ this.metadataTimestampsAheadOfRealData.get(msg.messageChainId).forEach((timestamp) => {
3496
+ this.metadataTimestampsAheadOfRealData.get(msg.messageChainId).delete(timestamp);
3497
+ });
3498
+ }
3499
+ }
3500
+ }
3501
+ createRemote(neighbor) {
3502
+ return new PlumtreeRpcRemote(this.localPeerDescriptor, neighbor, this.rpcCommunicator, PlumtreeRpcClient);
3503
+ }
3504
+ broadcast(msg, previousNode) {
3505
+ const messageChainId = msg.messageId.messageChainId;
3506
+ if (!this.latestMessages.has(messageChainId)) {
3507
+ this.latestMessages.set(messageChainId, []);
3508
+ }
3509
+ if (this.latestMessages.get(messageChainId).length < 20) {
3510
+ this.latestMessages.get(messageChainId).push(msg);
3511
+ }
3512
+ else {
3513
+ this.latestMessages.get(messageChainId).shift();
3514
+ this.latestMessages.get(messageChainId).push(msg);
3515
+ }
3516
+ if (this.metadataTimestampsAheadOfRealData.has(msg.messageId.messageChainId)) {
3517
+ this.metadataTimestampsAheadOfRealData.get(msg.messageId.messageChainId).delete(msg.messageId.timestamp);
3518
+ }
3519
+ this.emit('message', msg);
3520
+ const neighbors = this.neighbors.getAll().filter((neighbor) => dht.toNodeId(neighbor.getPeerDescriptor()) !== previousNode);
3521
+ for (const neighbor of neighbors) {
3522
+ if (this.localPausedNeighbors.isPaused(dht.toNodeId(neighbor.getPeerDescriptor()), msg.messageId.messageChainId)) {
3523
+ const remote = this.createRemote(neighbor.getPeerDescriptor());
3524
+ setImmediate(() => remote.sendMetadata(msg.messageId));
3525
+ }
3526
+ else {
3527
+ setImmediate(() => neighbor.sendStreamMessage(msg));
3528
+ }
3529
+ }
3530
+ }
3531
+ isNeighborPaused(node, msgChainId) {
3532
+ return this.localPausedNeighbors.isPaused(dht.toNodeId(node), msgChainId)
3533
+ || this.remotePausedNeighbors.isPaused(dht.toNodeId(node), msgChainId);
3534
+ }
3535
+ stop() {
3536
+ this.neighbors.off('nodeRemoved', this.onNeighborRemoved);
3537
+ }
3538
+ }
3539
+
3540
+ const createConfigWithDefaults = (options) => {
3541
+ const ownNodeId = dht.toNodeId(options.localPeerDescriptor);
3542
+ const rpcCommunicator = options.rpcCommunicator ?? new dht.ListeningRpcCommunicator(formStreamPartContentDeliveryServiceId(options.streamPartId), options.transport);
3543
+ const neighborTargetCount = options.neighborTargetCount ?? DEFAULT_NEIGHBOR_TARGET_COUNT;
3544
+ const maxContactCount = options.maxContactCount ?? DEFAULT_NODE_VIEW_SIZE;
3545
+ const acceptProxyConnections = options.acceptProxyConnections ?? DEFAULT_ACCEPT_PROXY_CONNECTIONS;
3546
+ const neighborUpdateInterval = options.neighborUpdateInterval ?? DEFAULT_NEIGHBOR_UPDATE_INTERVAL;
3547
+ const minPropagationTargets = options.minPropagationTargets ?? DEFAULT_MIN_PROPAGATION_TARGETS;
3548
+ const maxPropagationBufferSize = options.maxPropagationBufferSize ?? DEFAULT_MAX_PROPAGATION_BUFFER_SIZE;
3549
+ const neighbors = options.neighbors ?? new NodeList(ownNodeId, maxContactCount);
3550
+ const leftNodeView = options.leftNodeView ?? new NodeList(ownNodeId, maxContactCount);
3551
+ const rightNodeView = options.rightNodeView ?? new NodeList(ownNodeId, maxContactCount);
3552
+ const nearbyNodeView = options.nearbyNodeView ?? new NodeList(ownNodeId, maxContactCount);
3553
+ const randomNodeView = options.randomNodeView ?? new NodeList(ownNodeId, maxContactCount);
3554
+ const ongoingHandshakes = new Set();
3555
+ const temporaryConnectionRpcLocal = new TemporaryConnectionRpcLocal({
3556
+ rpcCommunicator,
3557
+ localPeerDescriptor: options.localPeerDescriptor,
3558
+ streamPartId: options.streamPartId,
3559
+ connectionLocker: options.connectionLocker
3560
+ });
3561
+ const proxyConnectionRpcLocal = acceptProxyConnections ? new ProxyConnectionRpcLocal({
3562
+ localPeerDescriptor: options.localPeerDescriptor,
3563
+ streamPartId: options.streamPartId,
3564
+ rpcCommunicator
3565
+ }) : undefined;
3566
+ const plumtreeManager = options.plumtreeOptimization ? new PlumtreeManager({
3567
+ neighbors,
3568
+ localPeerDescriptor: options.localPeerDescriptor,
3569
+ rpcCommunicator,
3570
+ maxPausedNeighbors: options.plumtreeMaxPausedNeighbors
3571
+ }) : undefined;
3572
+ const propagation = options.propagation ?? new Propagation({
3573
+ minPropagationTargets,
3574
+ maxMessages: maxPropagationBufferSize,
3575
+ ttl: DEFAULT_PROPAGATION_BUFFER_TTL,
3576
+ sendToNeighbor: async (neighborId, msg) => {
3577
+ const remote = neighbors.get(neighborId) ?? temporaryConnectionRpcLocal.getNodes().get(neighborId);
3578
+ const proxyConnection = proxyConnectionRpcLocal?.getConnection(neighborId);
3579
+ if (remote) {
3580
+ await remote.sendStreamMessage(msg, options.bufferWhileConnecting);
3581
+ }
3582
+ else if (proxyConnection) {
3583
+ await proxyConnection.remote.sendStreamMessage(msg);
3584
+ }
3585
+ else {
3586
+ throw new Error('Propagation target not found');
3587
+ }
3588
+ }
3589
+ });
3590
+ const handshaker = options.handshaker ?? new Handshaker({
3591
+ localPeerDescriptor: options.localPeerDescriptor,
3592
+ streamPartId: options.streamPartId,
3593
+ rpcCommunicator,
3594
+ neighbors,
3595
+ leftNodeView,
3596
+ rightNodeView,
3597
+ nearbyNodeView,
3598
+ randomNodeView,
3599
+ maxNeighborCount: neighborTargetCount,
3600
+ rpcRequestTimeout: options.rpcRequestTimeout,
3601
+ ongoingHandshakes
3602
+ });
3603
+ const neighborFinder = options.neighborFinder ?? new NeighborFinder({
3604
+ neighbors,
3605
+ leftNodeView,
3606
+ rightNodeView,
3607
+ nearbyNodeView,
3608
+ randomNodeView,
3609
+ doFindNeighbors: (excludedIds) => handshaker.attemptHandshakesOnContacts(excludedIds),
3610
+ minCount: neighborTargetCount
3611
+ });
3612
+ const neighborUpdateManager = options.neighborUpdateManager ?? new NeighborUpdateManager({
3613
+ neighbors,
3614
+ nearbyNodeView,
3615
+ localPeerDescriptor: options.localPeerDescriptor,
3616
+ neighborFinder,
3617
+ streamPartId: options.streamPartId,
3618
+ rpcCommunicator,
3619
+ neighborUpdateInterval,
3620
+ neighborTargetCount,
3621
+ ongoingHandshakes
3622
+ });
3623
+ const inspector = options.inspector ?? new Inspector({
3624
+ localPeerDescriptor: options.localPeerDescriptor,
3625
+ rpcCommunicator,
3626
+ streamPartId: options.streamPartId,
3627
+ connectionLocker: options.connectionLocker
3628
+ });
3629
+ return {
3630
+ ...options,
3631
+ neighbors,
3632
+ leftNodeView,
3633
+ rightNodeView,
3634
+ nearbyNodeView,
3635
+ randomNodeView,
3636
+ rpcCommunicator,
3637
+ handshaker,
3638
+ neighborFinder,
3639
+ neighborUpdateManager,
3640
+ propagation,
3641
+ neighborTargetCount,
3642
+ nodeViewSize: maxContactCount,
3643
+ proxyConnectionRpcLocal,
3644
+ inspector,
3645
+ temporaryConnectionRpcLocal,
3646
+ plumtreeManager
3647
+ };
3648
+ };
3649
+ const createContentDeliveryLayerNode = (options) => {
3650
+ return new ContentDeliveryLayerNode(createConfigWithDefaults(options));
3651
+ };
3652
+
3653
+ const logger$3 = new utils.Logger('ProxyConnectionRpcRemote');
3654
+ class ProxyConnectionRpcRemote extends dht.RpcRemote {
3655
+ async requestConnection(userId, direction) {
3656
+ const request = {
3657
+ direction,
3658
+ userId: utils.toUserIdRaw(userId)
3659
+ };
3660
+ const options = this.formDhtRpcOptions({
3661
+ timeout: dht.EXISTING_CONNECTION_TIMEOUT
3662
+ });
3663
+ try {
3664
+ const res = await this.getClient().requestConnection(request, options);
3665
+ return res.accepted;
3666
+ }
3667
+ catch (err) {
3668
+ logger$3.debug(`ProxyConnectionRequest failed with error`, { err });
3669
+ return false;
3670
+ }
3671
+ }
3672
+ }
3673
+
3674
+ // TODO use options option or named constant?
3675
+ const retry = async (task, description, abortSignal, delay = 10000) => {
3676
+ while (true) {
3677
+ try {
3678
+ const result = await task();
3679
+ return result;
3680
+ }
3681
+ catch {
3682
+ logger$2.warn(`Failed ${description} (retrying after delay)`, {
3683
+ delayInMs: delay
3684
+ });
3685
+ }
3686
+ await utils.wait(delay, abortSignal);
3687
+ }
3688
+ };
3689
+ const logger$2 = new utils.Logger('ProxyClient');
3690
+ const SERVICE_ID = 'system/proxy-client';
3691
+ class ProxyClient extends eventemitter3.EventEmitter {
3692
+ rpcCommunicator;
3693
+ contentDeliveryRpcLocal;
3694
+ options;
3695
+ duplicateDetectors = new Map();
3696
+ definition;
3697
+ connections = new Map();
3698
+ propagation;
3699
+ neighbors;
3700
+ abortController;
3701
+ constructor(options) {
3702
+ super();
3703
+ this.options = options;
3704
+ this.rpcCommunicator = new dht.ListeningRpcCommunicator(formStreamPartContentDeliveryServiceId(options.streamPartId), options.transport);
3705
+ // TODO use options option or named constant?
3706
+ this.neighbors = new NodeList(dht.toNodeId(this.options.localPeerDescriptor), 1000);
3707
+ this.contentDeliveryRpcLocal = new ContentDeliveryRpcLocal({
3708
+ localPeerDescriptor: this.options.localPeerDescriptor,
3709
+ streamPartId: this.options.streamPartId,
3710
+ markAndCheckDuplicate: (msg, prev) => markAndCheckDuplicate(this.duplicateDetectors, msg, prev),
3711
+ broadcast: (message, previousNode) => this.broadcast(message, previousNode),
3712
+ onLeaveNotice: (remoteNodeId) => {
3713
+ const contact = this.neighbors.get(remoteNodeId);
3714
+ if (contact) {
3715
+ // TODO should we catch possible promise rejection?
3716
+ setImmediate(() => this.onNodeDisconnected(contact.getPeerDescriptor()));
3717
+ }
3718
+ },
3719
+ rpcCommunicator: this.rpcCommunicator,
3720
+ markForInspection: () => { }
3721
+ });
3722
+ this.propagation = new Propagation({
3723
+ minPropagationTargets: options.minPropagationTargets,
3724
+ maxMessages: options.maxPropagationBufferSize,
3725
+ ttl: options.propagationBufferTtl,
3726
+ sendToNeighbor: async (neighborId, msg) => {
3727
+ const remote = this.neighbors.get(neighborId);
3728
+ if (remote) {
3729
+ await remote.sendStreamMessage(msg);
3730
+ }
3731
+ else {
3732
+ throw new Error('Propagation target not found');
3733
+ }
3734
+ }
3735
+ });
3736
+ this.abortController = new AbortController();
3737
+ }
3738
+ registerDefaultServerMethods() {
3739
+ this.rpcCommunicator.registerRpcNotification(StreamMessage, 'sendStreamMessage', (msg, context) => this.contentDeliveryRpcLocal.sendStreamMessage(msg, context));
3740
+ this.rpcCommunicator.registerRpcNotification(LeaveStreamPartNotice, 'leaveStreamPartNotice', (req, context) => this.contentDeliveryRpcLocal.leaveStreamPartNotice(req, context));
3741
+ }
3742
+ async setProxies(nodes, userId, direction, connectionCount) {
3743
+ logger$2.trace('Setting proxies', { streamPartId: this.options.streamPartId, peerDescriptors: nodes, direction, userId, connectionCount });
3744
+ if (connectionCount !== undefined && connectionCount > nodes.length) {
3745
+ throw new Error('Cannot set connectionCount above the size of the configured array of nodes');
3746
+ }
3747
+ const nodesIds = new Map();
3748
+ nodes.forEach((peerDescriptor) => {
3749
+ nodesIds.set(dht.toNodeId(peerDescriptor), peerDescriptor);
3750
+ });
3751
+ this.definition = {
3752
+ nodes: nodesIds,
3753
+ userId,
3754
+ direction,
3755
+ connectionCount: connectionCount ?? nodes.length
3756
+ };
3757
+ await this.updateConnections();
3758
+ }
3759
+ async updateConnections() {
3760
+ await Promise.all(this.getInvalidConnections().map(async (id) => {
3761
+ await this.closeConnection(id);
3762
+ }));
3763
+ const connectionCountDiff = this.definition.connectionCount - this.connections.size;
3764
+ if (connectionCountDiff > 0) {
3765
+ await this.openRandomConnections(connectionCountDiff);
3766
+ }
3767
+ else if (connectionCountDiff < 0) {
3768
+ await this.closeRandomConnections(-connectionCountDiff);
3769
+ }
3770
+ }
3771
+ getInvalidConnections() {
3772
+ return Array.from(this.connections.keys()).filter((id) => {
3773
+ return !this.definition.nodes.has(id)
3774
+ || this.definition.direction !== this.connections.get(id).direction;
3775
+ });
3776
+ }
3777
+ async openRandomConnections(connectionCount) {
3778
+ const proxiesToAttempt = sampleSize(Array.from(this.definition.nodes.keys()).filter((id) => !this.connections.has(id)), connectionCount);
3779
+ await Promise.all(proxiesToAttempt.map((id) => this.attemptConnection(id, this.definition.userId, this.definition.direction)));
3780
+ }
3781
+ async attemptConnection(nodeId, userId, direction) {
3782
+ const peerDescriptor = this.definition.nodes.get(nodeId);
3783
+ const rpcRemote = new ProxyConnectionRpcRemote(this.options.localPeerDescriptor, peerDescriptor, this.rpcCommunicator, ProxyConnectionRpcClient);
3784
+ const accepted = await rpcRemote.requestConnection(userId, direction);
3785
+ if (accepted) {
3786
+ this.options.connectionLocker.lockConnection(peerDescriptor, SERVICE_ID);
3787
+ this.connections.set(nodeId, { peerDescriptor, direction });
3788
+ const remote = new ContentDeliveryRpcRemote(this.options.localPeerDescriptor, peerDescriptor, this.rpcCommunicator, ContentDeliveryRpcClient);
3789
+ this.neighbors.add(remote);
3790
+ this.propagation.onNeighborJoined(nodeId);
3791
+ logger$2.info('Open proxy connection', {
3792
+ nodeId,
3793
+ streamPartId: this.options.streamPartId
3794
+ });
3795
+ }
3796
+ else {
3797
+ logger$2.warn('Unable to open proxy connection', {
3798
+ nodeId,
3799
+ streamPartId: this.options.streamPartId
3800
+ });
3801
+ }
3802
+ }
3803
+ async closeRandomConnections(connectionCount) {
3804
+ const proxiesToDisconnect = sampleSize(Array.from(this.connections.keys()), connectionCount);
3805
+ await Promise.allSettled(proxiesToDisconnect.map((node) => this.closeConnection(node)));
3806
+ }
3807
+ async closeConnection(nodeId) {
3808
+ if (this.connections.has(nodeId)) {
3809
+ logger$2.info('Close proxy connection', {
3810
+ nodeId
3811
+ });
3812
+ const server = this.neighbors.get(nodeId);
3813
+ server?.leaveStreamPartNotice(this.options.streamPartId, false);
3814
+ this.removeConnection(this.connections.get(nodeId).peerDescriptor);
3815
+ }
3816
+ }
3817
+ removeConnection(peerDescriptor) {
3818
+ const nodeId = dht.toNodeId(peerDescriptor);
3819
+ this.connections.delete(nodeId);
3820
+ this.neighbors.remove(nodeId);
3821
+ this.options.connectionLocker.unlockConnection(peerDescriptor, SERVICE_ID);
3822
+ }
3823
+ broadcast(msg, previousNode) {
3824
+ if (!previousNode) {
3825
+ markAndCheckDuplicate(this.duplicateDetectors, msg.messageId, msg.previousMessageRef);
3826
+ }
3827
+ this.emit('message', msg);
3828
+ this.propagation.feedUnseenMessage(msg, this.neighbors.getIds(), previousNode ?? null);
3829
+ }
3830
+ hasConnection(nodeId, direction) {
3831
+ return this.connections.has(nodeId) && this.connections.get(nodeId).direction === direction;
3832
+ }
3833
+ getDirection() {
3834
+ return this.definition.direction;
3835
+ }
3836
+ async onNodeDisconnected(peerDescriptor) {
3837
+ const nodeId = dht.toNodeId(peerDescriptor);
3838
+ if (this.connections.has(nodeId)) {
3839
+ this.options.connectionLocker.unlockConnection(peerDescriptor, SERVICE_ID);
3840
+ this.removeConnection(peerDescriptor);
3841
+ await retry(() => this.updateConnections(), 'updating proxy connections', this.abortController.signal);
3842
+ }
3843
+ }
3844
+ async start() {
3845
+ this.registerDefaultServerMethods();
3846
+ utils.addManagedEventListener(this.options.transport, 'disconnected',
3847
+ // TODO should we catch possible promise rejection?
3848
+ (peerDescriptor) => this.onNodeDisconnected(peerDescriptor), this.abortController.signal);
3849
+ }
3850
+ getDiagnosticInfo() {
3851
+ return {
3852
+ neighbors: this.neighbors.getAll().map((neighbor) => neighbor.getPeerDescriptor()),
3853
+ };
3854
+ }
3855
+ stop() {
3856
+ this.neighbors.getAll().forEach((remote) => {
3857
+ this.options.connectionLocker.unlockConnection(remote.getPeerDescriptor(), SERVICE_ID);
3858
+ remote.leaveStreamPartNotice(this.options.streamPartId, false);
3859
+ });
3860
+ this.neighbors.stop();
3861
+ this.rpcCommunicator.destroy();
3862
+ this.connections.clear();
3863
+ this.abortController.abort();
3864
+ }
3865
+ }
3866
+
3867
+ const logger$1 = new utils.Logger('ContentDeliveryManager');
3868
+ const streamPartIdToDataKey = (streamPartId) => {
3869
+ return dht.toDhtAddress(utils.computeSha1(streamPartId));
3870
+ };
3871
+ class ContentDeliveryManager extends eventemitter3.EventEmitter {
3872
+ transport;
3873
+ connectionLocker;
3874
+ controlLayerNode;
3875
+ metricsContext;
3876
+ metrics;
3877
+ options;
3878
+ streamParts;
3879
+ knownStreamPartEntryPoints = new Map();
3880
+ started = false;
3881
+ destroyed = false;
3882
+ constructor(options) {
3883
+ super();
3884
+ this.options = options;
3885
+ this.streamParts = new Map();
3886
+ this.metricsContext = options.metricsContext ?? new utils.MetricsContext();
3887
+ this.metrics = {
3888
+ broadcastMessagesPerSecond: new utils.RateMetric(),
3889
+ broadcastBytesPerSecond: new utils.RateMetric()
3890
+ };
3891
+ this.metricsContext.addMetrics('node', this.metrics);
3892
+ }
3893
+ async start(startedAndJoinedControlLayerNode, transport, connectionLocker) {
3894
+ if (this.started || this.destroyed) {
3895
+ return;
3896
+ }
3897
+ this.started = true;
3898
+ this.controlLayerNode = startedAndJoinedControlLayerNode;
3899
+ this.transport = transport;
3900
+ this.connectionLocker = connectionLocker;
3901
+ }
3902
+ async destroy() {
3903
+ if (!this.started || this.destroyed) {
3904
+ return;
3905
+ }
3906
+ logger$1.trace('Destroying ContentDeliveryManager');
3907
+ this.destroyed = true;
3908
+ await Promise.all(Array.from(this.streamParts.values()).map((streamPart) => streamPart.stop()));
3909
+ this.streamParts.clear();
3910
+ this.removeAllListeners();
3911
+ this.controlLayerNode = undefined;
3912
+ this.transport = undefined;
3913
+ this.connectionLocker = undefined;
3914
+ }
3915
+ broadcast(msg, streamPartDeliveryOptions) {
3916
+ const streamPartId = utils.toStreamPartID(msg.messageId.streamId, msg.messageId.streamPartition);
3917
+ logger$1.debug(`Broadcasting to stream part ${streamPartId}`);
3918
+ this.joinStreamPart(streamPartId, streamPartDeliveryOptions);
3919
+ this.streamParts.get(streamPartId).broadcast(msg);
3920
+ if (msg.body.oneofKind === 'contentMessage') {
3921
+ this.metrics.broadcastMessagesPerSecond.record(1);
3922
+ this.metrics.broadcastBytesPerSecond.record(msg.body.contentMessage.content.length);
3923
+ }
3924
+ }
3925
+ async leaveStreamPart(streamPartId) {
3926
+ const streamPart = this.streamParts.get(streamPartId);
3927
+ if (streamPart) {
3928
+ await streamPart.stop();
3929
+ this.streamParts.delete(streamPartId);
3930
+ }
3931
+ }
3932
+ joinStreamPart(streamPartId, streamPartDeliveryOptions) {
3933
+ let streamPart = this.streamParts.get(streamPartId);
3934
+ if (streamPart !== undefined) {
3935
+ return;
3936
+ }
3937
+ logger$1.debug(`Join stream part ${streamPartId}`);
3938
+ const discoveryLayerNode = this.createDiscoveryLayerNode(streamPartId, this.knownStreamPartEntryPoints.get(streamPartId) ?? []);
3939
+ const peerDescriptorStoreManager = new PeerDescriptorStoreManager({
3940
+ key: streamPartIdToDataKey(streamPartId),
3941
+ localPeerDescriptor: this.getPeerDescriptor(),
3942
+ fetchDataFromDht: (key) => this.controlLayerNode.fetchDataFromDht(key),
3943
+ storeDataToDht: (key, data) => this.controlLayerNode.storeDataToDht(key, data),
3944
+ deleteDataFromDht: async (key, waitForCompletion) => this.controlLayerNode.deleteDataFromDht(key, waitForCompletion)
3945
+ });
3946
+ const networkSplitAvoidance = new StreamPartNetworkSplitAvoidance({
3947
+ discoveryLayerNode,
3948
+ discoverEntryPoints: async () => peerDescriptorStoreManager.fetchNodes()
3949
+ });
3950
+ const node = this.createContentDeliveryLayerNode(streamPartId, discoveryLayerNode, () => peerDescriptorStoreManager.isLocalNodeStored(), streamPartDeliveryOptions);
3951
+ const streamPartReconnect = new StreamPartReconnect(discoveryLayerNode, peerDescriptorStoreManager);
3952
+ streamPart = {
3953
+ proxied: false,
3954
+ discoveryLayerNode,
3955
+ node,
3956
+ networkSplitAvoidance,
3957
+ broadcast: (msg) => node.broadcast(msg),
3958
+ stop: async () => {
3959
+ streamPartReconnect.destroy();
3960
+ networkSplitAvoidance.destroy();
3961
+ await peerDescriptorStoreManager.destroy();
3962
+ node.stop();
3963
+ await discoveryLayerNode.stop();
3964
+ },
3965
+ getDiagnosticInfo: () => node.getDiagnosticInfo()
3966
+ };
3967
+ this.streamParts.set(streamPartId, streamPart);
3968
+ node.on('message', (message) => {
3969
+ this.emit('newMessage', message);
3970
+ });
3971
+ const handleEntryPointLeave = async () => {
3972
+ if (this.destroyed || peerDescriptorStoreManager.isLocalNodeStored() || this.knownStreamPartEntryPoints.has(streamPartId)) {
3973
+ return;
3974
+ }
3975
+ const entryPoints = await peerDescriptorStoreManager.fetchNodes();
3976
+ if (entryPoints.length < MAX_NODE_COUNT) {
3977
+ await peerDescriptorStoreManager.storeAndKeepLocalNode();
3978
+ }
3979
+ };
3980
+ discoveryLayerNode.on('manualRejoinRequired', async () => {
3981
+ if (!streamPartReconnect.isRunning() && !networkSplitAvoidance.isRunning()) {
3982
+ logger$1.debug('Manual rejoin required for stream part', { streamPartId });
3983
+ await streamPartReconnect.reconnect();
3984
+ }
3985
+ });
3986
+ node.on('entryPointLeaveDetected', () => handleEntryPointLeave());
3987
+ setImmediate(async () => {
3988
+ try {
3989
+ await this.startLayersAndJoinDht(streamPartId, peerDescriptorStoreManager);
3990
+ }
3991
+ catch (err) {
3992
+ logger$1.warn(`Failed to join to stream part ${streamPartId}`, { err });
3993
+ }
3994
+ });
3995
+ }
3996
+ async startLayersAndJoinDht(streamPartId, peerDescriptorStoreManager) {
3997
+ logger$1.debug(`Start layers and join DHT for stream part ${streamPartId}`);
3998
+ const streamPart = this.streamParts.get(streamPartId);
3999
+ if ((streamPart === undefined) || streamPart.proxied) {
4000
+ // leaveStreamPart has been called (or leaveStreamPart called, and then setProxies called)
4001
+ return;
4002
+ }
4003
+ if (this.transport.isPrivateClientMode()) {
4004
+ await this.transport.disablePrivateClientMode();
4005
+ }
4006
+ await streamPart.discoveryLayerNode.start();
4007
+ await streamPart.node.start();
4008
+ const knownEntryPoints = this.knownStreamPartEntryPoints.get(streamPartId);
4009
+ if (knownEntryPoints !== undefined) {
4010
+ await Promise.all([
4011
+ streamPart.discoveryLayerNode.joinDht(knownEntryPoints),
4012
+ streamPart.discoveryLayerNode.joinRing()
4013
+ ]);
4014
+ }
4015
+ else {
4016
+ const entryPoints = await peerDescriptorStoreManager.fetchNodes();
4017
+ await Promise.all([
4018
+ streamPart.discoveryLayerNode.joinDht(sampleSize(entryPoints, MIN_NEIGHBOR_COUNT)),
4019
+ streamPart.discoveryLayerNode.joinRing()
4020
+ ]);
4021
+ if (entryPoints.length < MAX_NODE_COUNT) {
4022
+ await peerDescriptorStoreManager.storeAndKeepLocalNode();
4023
+ if (streamPart.discoveryLayerNode.getNeighborCount() < MIN_NEIGHBOR_COUNT) {
4024
+ setImmediate(() => streamPart.networkSplitAvoidance.avoidNetworkSplit());
4025
+ }
4026
+ }
4027
+ }
4028
+ }
4029
+ createDiscoveryLayerNode(streamPartId, entryPoints) {
4030
+ return new dht.DhtNode({
4031
+ transport: this.controlLayerNode,
4032
+ connectionsView: this.controlLayerNode.getConnectionsView(),
4033
+ serviceId: 'layer1::' + streamPartId,
4034
+ peerDescriptor: this.controlLayerNode.getLocalPeerDescriptor(),
4035
+ entryPoints,
4036
+ numberOfNodesPerKBucket: 4, // TODO use options option or named constant?
4037
+ rpcRequestTimeout: dht.EXISTING_CONNECTION_TIMEOUT,
4038
+ dhtJoinTimeout: 20000, // TODO use options option or named constant?
4039
+ periodicallyPingNeighbors: true,
4040
+ periodicallyPingRingContacts: true,
4041
+ neighborPingLimit: 16
4042
+ });
4043
+ }
4044
+ createContentDeliveryLayerNode(streamPartId, discoveryLayerNode, isLocalNodeEntryPoint, streamPartDeliveryOptions) {
4045
+ return createContentDeliveryLayerNode({
4046
+ streamPartId,
4047
+ transport: this.transport,
4048
+ discoveryLayerNode,
4049
+ connectionLocker: this.connectionLocker,
4050
+ localPeerDescriptor: this.controlLayerNode.getLocalPeerDescriptor(),
4051
+ minPropagationTargets: this.options.streamPartitionMinPropagationTargets,
4052
+ neighborTargetCount: this.options.streamPartitionNeighborTargetCount,
4053
+ maxPropagationBufferSize: this.options.streamPartitionMaxPropagationBufferSize,
4054
+ acceptProxyConnections: this.options.acceptProxyConnections,
4055
+ rpcRequestTimeout: this.options.rpcRequestTimeout,
4056
+ neighborUpdateInterval: this.options.neighborUpdateInterval,
4057
+ isLocalNodeEntryPoint,
4058
+ bufferWhileConnecting: this.options.bufferWhileConnecting,
4059
+ plumtreeOptimization: streamPartDeliveryOptions?.plumtreeOptimization?.enabled,
4060
+ plumtreeMaxPausedNeighbors: streamPartDeliveryOptions?.plumtreeOptimization?.enabled === true ?
4061
+ streamPartDeliveryOptions?.plumtreeOptimization?.maxPausedNeighbors : undefined
4062
+ });
4063
+ }
4064
+ async setProxies(streamPartId, nodes, userId, direction, connectionCount) {
4065
+ // TODO explicit default value for "acceptProxyConnections" or make it required
4066
+ if (this.options.acceptProxyConnections) {
4067
+ throw new Error('cannot set proxies when acceptProxyConnections=true');
4068
+ }
4069
+ const enable = (nodes.length > 0) && ((connectionCount === undefined) || (connectionCount > 0));
4070
+ if (enable) {
4071
+ let client;
4072
+ const alreadyProxied = this.isProxiedStreamPart(streamPartId);
4073
+ if (alreadyProxied) {
4074
+ client = this.streamParts.get(streamPartId).client;
4075
+ }
4076
+ else {
4077
+ client = this.createProxyClient(streamPartId);
4078
+ this.streamParts.set(streamPartId, {
4079
+ proxied: true,
4080
+ client,
4081
+ broadcast: (msg) => client.broadcast(msg),
4082
+ stop: async () => client.stop(),
4083
+ getDiagnosticInfo: () => client.getDiagnosticInfo()
4084
+ });
4085
+ client.on('message', (message) => {
4086
+ this.emit('newMessage', message);
4087
+ });
4088
+ if (Array.from(this.streamParts.values()).every((streamPart) => streamPart.proxied)) {
4089
+ await this.transport.enablePrivateClientMode();
4090
+ }
4091
+ await client.start();
4092
+ }
4093
+ await client.setProxies(nodes, userId, direction, connectionCount);
4094
+ }
4095
+ else {
4096
+ await this.streamParts.get(streamPartId)?.stop();
4097
+ this.streamParts.delete(streamPartId);
4098
+ }
4099
+ }
4100
+ createProxyClient(streamPartId) {
4101
+ return new ProxyClient({
4102
+ transport: this.transport,
4103
+ localPeerDescriptor: this.controlLayerNode.getLocalPeerDescriptor(),
4104
+ streamPartId,
4105
+ connectionLocker: this.connectionLocker,
4106
+ minPropagationTargets: this.options.streamPartitionMinPropagationTargets ?? DEFAULT_MIN_PROPAGATION_TARGETS,
4107
+ maxPropagationBufferSize: this.options.streamPartitionMaxPropagationBufferSize ?? DEFAULT_MAX_PROPAGATION_BUFFER_SIZE,
4108
+ propagationBufferTtl: DEFAULT_PROPAGATION_BUFFER_TTL
4109
+ });
4110
+ }
4111
+ async inspect(peerDescriptor, streamPartId) {
4112
+ const streamPart = this.streamParts.get(streamPartId);
4113
+ if ((streamPart !== undefined) && !streamPart.proxied) {
4114
+ return streamPart.node.inspect(peerDescriptor);
4115
+ }
4116
+ return false;
4117
+ }
4118
+ // TODO inline this method?
4119
+ getNodeInfo() {
4120
+ const streamParts = Array.from(this.streamParts.entries()).filter(([_, node]) => node.proxied === false);
4121
+ return streamParts.map(([streamPartId]) => {
4122
+ const stream = this.streamParts.get(streamPartId);
4123
+ return {
4124
+ id: streamPartId,
4125
+ controlLayerNeighbors: stream.discoveryLayerNode.getNeighbors(),
4126
+ deprecatedContentDeliveryLayerNeighbors: [],
4127
+ contentDeliveryLayerNeighbors: stream.node.getInfos()
4128
+ };
4129
+ });
4130
+ }
4131
+ setStreamPartEntryPoints(streamPartId, entryPoints) {
4132
+ this.knownStreamPartEntryPoints.set(streamPartId, entryPoints);
4133
+ }
4134
+ isProxiedStreamPart(streamPartId, direction) {
4135
+ const streamPart = this.streamParts.get(streamPartId);
4136
+ return (streamPart !== undefined)
4137
+ && streamPart.proxied
4138
+ && ((direction === undefined) || (streamPart.client.getDirection() === direction));
4139
+ }
4140
+ getStreamPartDelivery(streamPartId) {
4141
+ return this.streamParts.get(streamPartId);
4142
+ }
4143
+ hasStreamPart(streamPartId) {
4144
+ return this.streamParts.has(streamPartId);
4145
+ }
4146
+ getPeerDescriptor() {
4147
+ return this.controlLayerNode.getLocalPeerDescriptor();
4148
+ }
4149
+ getNodeId() {
4150
+ return dht.toNodeId(this.controlLayerNode.getLocalPeerDescriptor());
4151
+ }
4152
+ getNeighbors(streamPartId) {
4153
+ const streamPart = this.streamParts.get(streamPartId);
4154
+ return streamPart?.proxied === false
4155
+ ? streamPart.node.getNeighbors().map((n) => dht.toNodeId(n))
4156
+ : [];
4157
+ }
4158
+ getStreamParts() {
4159
+ return Array.from(this.streamParts.keys()).map((id) => utils.StreamPartIDUtils.parse(id));
4160
+ }
4161
+ getDiagnosticInfo() {
4162
+ return {
4163
+ streamParts: this.getStreamParts().map((id) => {
4164
+ return {
4165
+ id,
4166
+ info: this.getStreamPartDelivery(id).getDiagnosticInfo()
4167
+ };
4168
+ })
4169
+ };
4170
+ }
4171
+ }
4172
+
4173
+ class NodeInfoRpcRemote extends dht.RpcRemote {
4174
+ async getInfo() {
4175
+ return this.getClient().getInfo({}, this.formDhtRpcOptions());
4176
+ }
4177
+ }
4178
+
4179
+ class NodeInfoClient {
4180
+ ownPeerDescriptor;
4181
+ rpcCommunicator;
4182
+ constructor(ownPeerDescriptor, rpcCommunicator) {
4183
+ this.ownPeerDescriptor = ownPeerDescriptor;
4184
+ this.rpcCommunicator = rpcCommunicator;
4185
+ }
4186
+ async getInfo(node) {
4187
+ const remote = new NodeInfoRpcRemote(this.ownPeerDescriptor, node, this.rpcCommunicator, NodeInfoRpcClient);
4188
+ // TODO remove casting when we validate NodeInfoResponse messages and therefore can annotate
4189
+ // each of the field as required in the decorated type
4190
+ return remote.getInfo();
4191
+ }
4192
+ }
4193
+
4194
+ const NODE_INFO_RPC_SERVICE_ID = 'system/node-info-rpc';
4195
+ class NodeInfoRpcLocal {
4196
+ stack;
4197
+ rpcCommunicator;
4198
+ constructor(stack, rpcCommunicator) {
4199
+ this.stack = stack;
4200
+ this.rpcCommunicator = rpcCommunicator;
4201
+ this.registerDefaultServerMethods();
4202
+ }
4203
+ registerDefaultServerMethods() {
4204
+ this.rpcCommunicator.registerRpcMethod(NodeInfoRequest, NodeInfoResponse, 'getInfo', () => this.getInfo());
4205
+ }
4206
+ async getInfo() {
4207
+ return this.stack.createNodeInfo();
4208
+ }
4209
+ }
4210
+
4211
+ const logger = new utils.Logger('NetworkStack');
4212
+ const instances = [];
4213
+ const stopInstances = async () => {
4214
+ // make a clone so that it is ok for each instance.stop() to remove itself from the list (at line 139)
4215
+ // while the map function is iterating the list
4216
+ const clonedInstances = [...instances];
4217
+ await Promise.all(clonedInstances.map((instance) => instance.stop()));
4218
+ };
4219
+ /**
4220
+ * @todo The following cleanup logic is currently handled inside this module for both Node.js and
4221
+ * browser environments. Consider refactoring it into a higher-level integration layer if lifecycle
4222
+ * management is centralized elsewhere.
4223
+ */
4224
+ if (typeof process === 'object' && typeof process?.on === 'function') {
4225
+ /**
4226
+ * @todo The `exit` event shouldn't use an async handler because the event loop is already
4227
+ * shutting down, so async work won't complete. Calling `process.exit()` inside an `exit`
4228
+ * handler is also redundant and may cause issues. Remove `exit` from `EXIT_EVENTS`
4229
+ * or omit it entirely, since other signal handlers already terminate the process.
4230
+ */
4231
+ const EXIT_EVENTS = [`exit`, `SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `unhandledRejection`, `SIGTERM`];
4232
+ EXIT_EVENTS.forEach((event) => {
4233
+ /**
4234
+ * @todo Registering handlers at module load time can cause side effects. Use explicit
4235
+ * or lazy initialization to improve control and testability.
4236
+ */
4237
+ process.on(event, async (eventArg) => {
4238
+ const isError = (event === 'uncaughtException') || (event === 'unhandledRejection');
4239
+ if (isError) {
4240
+ logger.error(`exit event: ${event}`, eventArg);
4241
+ }
4242
+ /**
4243
+ * @todo Async `stopInstances()` may be interrupted by `process.exit()`. Use
4244
+ * synchronous cleanup or a timeout, and wait for cleanup on graceful signals
4245
+ * but exit quickly on error events.
4246
+ */
4247
+ await stopInstances();
4248
+ process.exit(isError ? 1 : 0);
4249
+ });
4250
+ });
4251
+ }
4252
+ if (typeof window === 'object') {
4253
+ /**
4254
+ * @todo Registering handlers at module load time can cause side effects. Use explicit
4255
+ * or lazy initialization to improve control and testability.
4256
+ */
4257
+ window.addEventListener('unload', async () => {
4258
+ await stopInstances();
4259
+ });
4260
+ }
4261
+ class NetworkStack {
4262
+ controlLayerNode;
4263
+ contentDeliveryManager;
4264
+ stopped = false;
4265
+ metricsContext;
4266
+ options;
4267
+ nodeInfoRpcLocal;
4268
+ nodeInfoClient;
4269
+ constructor(options) {
4270
+ this.options = options;
4271
+ this.metricsContext = options.metricsContext ?? new utils.MetricsContext();
4272
+ this.controlLayerNode = new dht.DhtNode({
4273
+ ...options.layer0,
4274
+ metricsContext: this.metricsContext,
4275
+ allowIncomingPrivateConnections: options.networkNode?.acceptProxyConnections
4276
+ });
4277
+ this.contentDeliveryManager = new ContentDeliveryManager({
4278
+ ...options.networkNode,
4279
+ metricsContext: this.metricsContext
4280
+ });
4281
+ instances.push(this);
4282
+ }
4283
+ async joinStreamPart(streamPartId, neighborRequirement, streamPartDeliveryOptions) {
4284
+ if (this.getContentDeliveryManager().isProxiedStreamPart(streamPartId)) {
4285
+ throw new Error(`Cannot join to ${streamPartId} as proxy connections have been set`);
4286
+ }
4287
+ await this.ensureConnectedToControlLayer();
4288
+ this.getContentDeliveryManager().joinStreamPart(streamPartId, streamPartDeliveryOptions);
4289
+ if (neighborRequirement !== undefined) {
4290
+ await utils.until(() => {
4291
+ return this.getContentDeliveryManager().getNeighbors(streamPartId).length >= neighborRequirement.minCount;
4292
+ }, neighborRequirement.timeout);
4293
+ }
4294
+ }
4295
+ async broadcast(msg, streamPartDeliveryOptions) {
4296
+ const streamPartId = utils.toStreamPartID(msg.messageId.streamId, msg.messageId.streamPartition);
4297
+ if (this.getContentDeliveryManager().isProxiedStreamPart(streamPartId, exports.ProxyDirection.SUBSCRIBE)
4298
+ && (msg.body.oneofKind === 'contentMessage')) {
4299
+ throw new Error(`Cannot broadcast to ${streamPartId} as proxy subscribe connections have been set`);
4300
+ }
4301
+ // TODO could combine these two calls to isProxiedStreamPart?
4302
+ if (!this.contentDeliveryManager.isProxiedStreamPart(streamPartId)) {
4303
+ await this.ensureConnectedToControlLayer();
4304
+ }
4305
+ this.getContentDeliveryManager().broadcast(msg, streamPartDeliveryOptions);
4306
+ }
4307
+ async start(doJoin = true) {
4308
+ logger.info('Starting a Streamr Network Node');
4309
+ await this.controlLayerNode.start();
4310
+ logger.info(`Node id is ${dht.toNodeId(this.controlLayerNode.getLocalPeerDescriptor())}`);
4311
+ const connectionManager = this.controlLayerNode.getTransport();
4312
+ if ((this.options.layer0?.entryPoints?.some((entryPoint) => dht.areEqualPeerDescriptors(entryPoint, this.controlLayerNode.getLocalPeerDescriptor())))) {
4313
+ await this.controlLayerNode?.joinDht(this.options.layer0.entryPoints);
4314
+ }
4315
+ else if (doJoin) {
4316
+ // in practice there aren't be existing connections and therefore this always connects
4317
+ await this.ensureConnectedToControlLayer();
4318
+ }
4319
+ // TODO: remove undefined checks here. Assume that start is approproately awaited before stop is called.
4320
+ await this.contentDeliveryManager?.start(this.controlLayerNode, connectionManager, connectionManager);
4321
+ if (this.contentDeliveryManager) {
4322
+ const infoRpcCommunicator = new dht.ListeningRpcCommunicator(NODE_INFO_RPC_SERVICE_ID, this.getConnectionManager());
4323
+ this.nodeInfoRpcLocal = new NodeInfoRpcLocal(this, infoRpcCommunicator);
4324
+ this.nodeInfoClient = new NodeInfoClient(this.controlLayerNode.getLocalPeerDescriptor(), infoRpcCommunicator);
4325
+ }
4326
+ }
4327
+ async ensureConnectedToControlLayer() {
4328
+ // TODO we could wrap joinDht with pOnce and call it here (no else-if needed in that case)
4329
+ if (!this.controlLayerNode.hasJoined()) {
4330
+ setImmediate(async () => {
4331
+ if (this.options.layer0?.entryPoints !== undefined) {
4332
+ // TODO should catch possible rejection?
4333
+ // the question mark is there to avoid problems when stop() is called before start()
4334
+ // -> TODO change to exlamation mark if we don't support that (and remove NetworkStackStoppedDuringStart.test)
4335
+ await this.controlLayerNode?.joinDht(this.options.layer0.entryPoints);
4336
+ }
4337
+ });
4338
+ await this.controlLayerNode.waitForNetworkConnectivity();
4339
+ }
4340
+ }
4341
+ getContentDeliveryManager() {
4342
+ return this.contentDeliveryManager;
4343
+ }
4344
+ getControlLayerNode() {
4345
+ return this.controlLayerNode;
4346
+ }
4347
+ getMetricsContext() {
4348
+ return this.metricsContext;
4349
+ }
4350
+ async fetchNodeInfo(node) {
4351
+ if (!dht.areEqualPeerDescriptors(node, this.getControlLayerNode().getLocalPeerDescriptor())) {
4352
+ return this.nodeInfoClient.getInfo(node);
4353
+ }
4354
+ else {
4355
+ return this.createNodeInfo();
4356
+ }
4357
+ }
4358
+ createNodeInfo() {
4359
+ return {
4360
+ peerDescriptor: this.getControlLayerNode().getLocalPeerDescriptor(),
4361
+ controlLayer: {
4362
+ connections: this.getControlLayerNode().getConnectionsView().getConnections(),
4363
+ neighbors: this.getControlLayerNode().getNeighbors()
4364
+ },
4365
+ streamPartitions: this.getContentDeliveryManager().getNodeInfo(),
4366
+ applicationVersion: version
4367
+ };
4368
+ }
4369
+ getOptions() {
4370
+ return this.options;
4371
+ }
4372
+ getConnectionManager() {
4373
+ return this.controlLayerNode.getTransport();
4374
+ }
4375
+ async stop() {
4376
+ if (!this.stopped) {
4377
+ this.stopped = true;
4378
+ pull(instances, this);
4379
+ await this.contentDeliveryManager.destroy();
4380
+ await this.controlLayerNode.stop();
4381
+ this.contentDeliveryManager = undefined;
4382
+ this.controlLayerNode = undefined;
4383
+ }
4384
+ }
4385
+ }
4386
+
4387
+ const createNetworkNode = (opts) => {
4388
+ return new NetworkNode(new NetworkStack(opts));
4389
+ };
4390
+ /**
4391
+ * Convenience wrapper for building client-facing functionality. Used by client.
4392
+ */
4393
+ class NetworkNode {
4394
+ stack;
4395
+ stopped = false;
4396
+ externalNetworkRpc;
4397
+ /** @internal */
4398
+ constructor(stack) {
4399
+ this.stack = stack;
4400
+ }
4401
+ async start(doJoin) {
4402
+ await this.stack.start(doJoin);
4403
+ this.externalNetworkRpc = new ExternalNetworkRpc(this.stack.getControlLayerNode().getTransport());
4404
+ }
4405
+ async inspect(node, streamPartId) {
4406
+ return this.stack.getContentDeliveryManager().inspect(node, streamPartId);
4407
+ }
4408
+ async broadcast(msg, streamPartDeliveryOptions) {
4409
+ await this.stack.broadcast(msg, streamPartDeliveryOptions);
4410
+ }
4411
+ async join(streamPartId, neighborRequirement, streamPartDeliveryOptions) {
4412
+ await this.stack.joinStreamPart(streamPartId, neighborRequirement, streamPartDeliveryOptions);
4413
+ }
4414
+ async setProxies(streamPartId, nodes, userId, direction, connectionCount) {
4415
+ await this.stack.getContentDeliveryManager().setProxies(streamPartId, nodes, userId, direction, connectionCount);
4416
+ }
4417
+ isProxiedStreamPart(streamPartId) {
4418
+ return this.stack.getContentDeliveryManager().isProxiedStreamPart(streamPartId);
4419
+ }
4420
+ addMessageListener(listener) {
4421
+ this.stack.getContentDeliveryManager().on('newMessage', listener);
4422
+ }
4423
+ setStreamPartEntryPoints(streamPartId, contactPeerDescriptors) {
4424
+ this.stack.getContentDeliveryManager().setStreamPartEntryPoints(streamPartId, contactPeerDescriptors);
4425
+ }
4426
+ removeMessageListener(listener) {
4427
+ this.stack.getContentDeliveryManager().off('newMessage', listener);
4428
+ }
4429
+ async leave(streamPartId) {
4430
+ if (this.stopped) {
4431
+ return;
4432
+ }
4433
+ await this.stack.getContentDeliveryManager().leaveStreamPart(streamPartId);
4434
+ }
4435
+ getNeighbors(streamPartId) {
4436
+ return this.stack.getContentDeliveryManager().getNeighbors(streamPartId);
4437
+ }
4438
+ hasStreamPart(streamPartId) {
4439
+ return this.stack.getContentDeliveryManager().hasStreamPart(streamPartId);
4440
+ }
4441
+ async stop() {
4442
+ this.stopped = true;
4443
+ this.externalNetworkRpc.destroy();
4444
+ await this.stack.stop();
4445
+ }
4446
+ getPeerDescriptor() {
4447
+ return this.stack.getControlLayerNode().getLocalPeerDescriptor();
4448
+ }
4449
+ getMetricsContext() {
4450
+ return this.stack.getMetricsContext();
4451
+ }
4452
+ getNodeId() {
4453
+ return this.stack.getContentDeliveryManager().getNodeId();
4454
+ }
4455
+ getOptions() {
4456
+ return this.stack.getOptions();
4457
+ }
4458
+ getStreamParts() {
4459
+ return this.stack.getContentDeliveryManager().getStreamParts();
4460
+ }
4461
+ async fetchNodeInfo(node) {
4462
+ return this.stack.fetchNodeInfo(node);
4463
+ }
4464
+ getDiagnosticInfo() {
4465
+ return {
4466
+ controlLayer: this.stack.getControlLayerNode().getDiagnosticInfo(),
4467
+ contentLayer: this.stack.getContentDeliveryManager().getDiagnosticInfo()
4468
+ };
4469
+ }
4470
+ registerExternalNetworkRpcMethod(request, response, name, fn) {
4471
+ this.externalNetworkRpc.registerRpcMethod(request, response, name, fn);
4472
+ }
4473
+ createExternalRpcClient(clientClass) {
4474
+ return this.externalNetworkRpc.createRpcClient(clientClass);
4475
+ }
4476
+ }
4477
+
4478
+ exports.ControlLayerInfo = ControlLayerInfo;
4479
+ exports.EncryptedGroupKey = EncryptedGroupKey;
4480
+ exports.GroupKeyRequest = GroupKeyRequest;
4481
+ exports.GroupKeyResponse = GroupKeyResponse;
4482
+ exports.MessageID = MessageID;
4483
+ exports.MessageRef = MessageRef;
4484
+ exports.NetworkNode = NetworkNode;
4485
+ exports.NetworkStack = NetworkStack;
4486
+ exports.StreamMessage = StreamMessage;
4487
+ exports.createNetworkNode = createNetworkNode;
4488
+ exports.streamPartIdToDataKey = streamPartIdToDataKey;
4489
+ //# sourceMappingURL=exports.cjs.map