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