@hive-p2p/server 1.0.36 → 1.0.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/topologist.mjs +27 -23
- package/package.json +1 -1
- package/rendering/NetworkRenderer.mjs +1 -1
package/core/topologist.mjs
CHANGED
|
@@ -47,21 +47,25 @@ class OfferQueue {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
export class Topologist {
|
|
50
|
-
id; cryptoCodex; gossip; messager; peerStore; bootstraps;
|
|
51
|
-
halfTarget = Math.ceil(DISCOVERY.TARGET_NEIGHBORS_COUNT / 2);
|
|
52
|
-
twiceTarget = DISCOVERY.TARGET_NEIGHBORS_COUNT * 2;
|
|
50
|
+
id; cryptoCodex; gossip; messager; peerStore; bootstraps; offersQueue = new OfferQueue();
|
|
53
51
|
/** @type {Map<string, boolean>} */ bootstrapsConnectionState = new Map();
|
|
54
|
-
|
|
55
|
-
get isPublicNode() { return this.services?.publicUrl ? true : false; }
|
|
56
52
|
/** @type {import('./node-services.mjs').NodeServices | undefined} */ services;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
/** @type {number} */ NEIGHBORS_TARGET;
|
|
54
|
+
/** @type {number} */ HALF_TARGET;
|
|
55
|
+
/** @type {number} */ TWICE_TARGET;
|
|
56
|
+
setNeighborsTarget(count = DISCOVERY.TARGET_NEIGHBORS_COUNT) { // Setter for hot swapping
|
|
57
|
+
this.NEIGHBORS_TARGET = count;
|
|
58
|
+
this.HALF_TARGET = Math.ceil(this.NEIGHBORS_TARGET / 2);
|
|
59
|
+
this.TWICE_TARGET = this.NEIGHBORS_TARGET * 2;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
phase = 0; nextBootstrapIndex = 0;
|
|
61
63
|
maxBonus = NODE.CONNECTION_UPGRADE_TIMEOUT * .2; // 20% of 15sec: 3sec max
|
|
64
|
+
get isPublicNode() { return this.services?.publicUrl ? true : false; }
|
|
62
65
|
|
|
63
66
|
/** @param {string} selfId @param {import('./crypto-codex.mjs').CryptoCodex} cryptoCodex @param {import('./gossip.mjs').Gossip} gossip @param {import('./unicast.mjs').UnicastMessager} messager @param {import('./peer-store.mjs').PeerStore} peerStore @param {string[]} bootstraps */
|
|
64
67
|
constructor(selfId, cryptoCodex, gossip, messager, peerStore, bootstraps) {
|
|
68
|
+
this.setNeighborsTarget(DISCOVERY.TARGET_NEIGHBORS_COUNT);
|
|
65
69
|
this.id = selfId; this.cryptoCodex = cryptoCodex; this.gossip = gossip; this.messager = messager; this.peerStore = peerStore;
|
|
66
70
|
for (const url of bootstraps) this.bootstrapsConnectionState.set(url, false);
|
|
67
71
|
this.bootstraps = [...bootstraps].sort(() => Math.random() - 0.5); // shuffle
|
|
@@ -71,7 +75,7 @@ export class Topologist {
|
|
|
71
75
|
// PUBLIC METHODS
|
|
72
76
|
tick() {
|
|
73
77
|
const { neighborsCount, nonPublicNeighborsCount, isEnough, isTooMany, isHalfReached } = this.#localTopologyInfo;
|
|
74
|
-
const offersToCreate = nonPublicNeighborsCount >=
|
|
78
|
+
const offersToCreate = nonPublicNeighborsCount >= this.NEIGHBORS_TARGET / 3 ? 1 : TRANSPORTS.MAX_SDP_OFFERS;
|
|
75
79
|
this.peerStore.offerManager.offersToCreate = isEnough ? 0 : offersToCreate;
|
|
76
80
|
if (this.isPublicNode) { this.services.freePublicNodeByKickingPeers(); return; } // public nodes don't need more connections
|
|
77
81
|
if (isTooMany) return Math.random() > .05 ? this.#improveTopologyByKickingPeers() : null;
|
|
@@ -95,15 +99,15 @@ export class Topologist {
|
|
|
95
99
|
if (signal.type === 'answer') { // ANSWER SHORT CIRCUIT => Rich should connect poor, and poor should connect rich.
|
|
96
100
|
if (this.peerStore.addConnectingPeer(senderId, signal, offerHash) !== true) return;
|
|
97
101
|
const delta = Math.abs(nonPublicNeighborsCount - this.#getOverlap(senderId).nonPublicCount);
|
|
98
|
-
const bonusPerDeltaPoint = this.maxBonus /
|
|
102
|
+
const bonusPerDeltaPoint = this.maxBonus / this.NEIGHBORS_TARGET; // from 0 to maxBonus
|
|
99
103
|
const bonus = Math.round(Math.min(this.maxBonus, delta * bonusPerDeltaPoint));
|
|
100
104
|
return this.peerStore.assignSignal(senderId, signal, offerHash, CLOCK.time + bonus);
|
|
101
105
|
}
|
|
102
106
|
|
|
103
107
|
// OFFER
|
|
104
|
-
if (nonPublicNeighborsCount > this.
|
|
108
|
+
if (nonPublicNeighborsCount > this.TWICE_TARGET) return; // we are over connected, ignore the offer
|
|
105
109
|
const { overlap, nonPublicCount } = this.#getOverlap(senderId);
|
|
106
|
-
if (nonPublicCount > this.
|
|
110
|
+
if (nonPublicCount > this.TWICE_TARGET) return; // the sender is over connected, ignore the offer
|
|
107
111
|
|
|
108
112
|
const offerItem = { senderId, data, overlap, neighborsCount: nonPublicCount, timestamp: CLOCK.time };
|
|
109
113
|
this.offersQueue.updateOrderingBy(isHalfReached);
|
|
@@ -116,9 +120,10 @@ export class Topologist {
|
|
|
116
120
|
for (const id in this.peerStore.connecting) connectingCount++;
|
|
117
121
|
|
|
118
122
|
// MINIMIZE BOOTSTRAP CONNECTIONS DEPENDING ON HOW MANY NEIGHBORS WE HAVE
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
|
|
123
|
+
if (this.NEIGHBORS_TARGET || neighborsCount) // BYPASS IF NO TARGET => AUTHORIZE BOOTSTRAP CONNECTION
|
|
124
|
+
if (publicConnectedCount >= this.HALF_TARGET) return; // already connected to enough bootstraps
|
|
125
|
+
else if (neighborsCount >= this.NEIGHBORS_TARGET) return; // no more bootstrap needed
|
|
126
|
+
else if (connectingCount + nonPublicNeighborsCount > this.TWICE_TARGET) return; // no more bootstrap needed
|
|
122
127
|
|
|
123
128
|
const publicUrl = this.bootstraps[this.nextBootstrapIndex++ % this.bootstraps.length];
|
|
124
129
|
if (this.bootstrapsConnectionState.get(publicUrl)) return; // already connecting/connected
|
|
@@ -131,9 +136,9 @@ export class Topologist {
|
|
|
131
136
|
connected: this.peerStore.connected,
|
|
132
137
|
neighborsCount: this.peerStore.neighborsList.length,
|
|
133
138
|
nonPublicNeighborsCount: this.peerStore.standardNeighborsList.length,
|
|
134
|
-
isEnough: this.peerStore.standardNeighborsList.length >=
|
|
135
|
-
isTooMany: this.peerStore.standardNeighborsList.length >
|
|
136
|
-
isHalfReached: this.peerStore.standardNeighborsList.length >= this.
|
|
139
|
+
isEnough: this.peerStore.standardNeighborsList.length >= this.NEIGHBORS_TARGET,
|
|
140
|
+
isTooMany: this.peerStore.standardNeighborsList.length > this.NEIGHBORS_TARGET,
|
|
141
|
+
isHalfReached: this.peerStore.standardNeighborsList.length >= this.HALF_TARGET,
|
|
137
142
|
}
|
|
138
143
|
}
|
|
139
144
|
#getOverlap(peerId1 = 'toto') {
|
|
@@ -212,7 +217,7 @@ export class Topologist {
|
|
|
212
217
|
if (!offerHash || !readyOffer) return; // no ready offer to spread
|
|
213
218
|
|
|
214
219
|
// IF WE ARE CONNECTED TO LESS 2 (WRTC) AND NOT TO MUCH CONNECTING, WE CAN BROADCAST IT TO ALL
|
|
215
|
-
if (!isHalfReached && ingPlusEd <= this.
|
|
220
|
+
if (!isHalfReached && ingPlusEd <= this.TWICE_TARGET) {
|
|
216
221
|
this.gossip.broadcastToAll({ signal: readyOffer.signal, offerHash }, 'signal_offer');
|
|
217
222
|
readyOffer.sentCounter++; // avoid sending it again
|
|
218
223
|
return; // limit to one per loop
|
|
@@ -236,7 +241,7 @@ export class Topologist {
|
|
|
236
241
|
else if (this.peerStore.connected[id] || this.peerStore.connecting[id]) continue;
|
|
237
242
|
|
|
238
243
|
const { overlap, nonPublicCount } = this.#getOverlap(id);
|
|
239
|
-
if (nonPublicCount >
|
|
244
|
+
if (nonPublicCount > this.NEIGHBORS_TARGET) continue; // the peer is over connected, ignore it
|
|
240
245
|
if (bestValue === null) bestValue = isHalfReached ? overlap : nonPublicCount;
|
|
241
246
|
if (isHalfReached && overlap > bestValue) continue; // only target lowest overlap
|
|
242
247
|
if (!isHalfReached && nonPublicCount < bestValue) continue; // only target highest neighbors count
|
|
@@ -264,7 +269,7 @@ export class Topologist {
|
|
|
264
269
|
this.offersQueue.updateOrderingBy(this.#localTopologyInfo.isHalfReached);
|
|
265
270
|
this.offersQueue.removeOlderThan(TRANSPORTS.SDP_OFFER_EXPIRATION / 2); // remove close to expiration offers
|
|
266
271
|
for (let i = 0; i < this.offersQueue.size; i++) {
|
|
267
|
-
//if (connectingCount > this.
|
|
272
|
+
//if (connectingCount > this.TWICE_TARGET * 2) break; // stop if we are over connecting
|
|
268
273
|
const { senderId, data, timestamp, value } = this.offersQueue.bestOfferInfo;
|
|
269
274
|
if (!senderId || !data || !timestamp) break;
|
|
270
275
|
if (this.peerStore.connected[senderId] || this.peerStore.isKicked(senderId)) continue;
|
|
@@ -277,7 +282,6 @@ export class Topologist {
|
|
|
277
282
|
connectingCount++;
|
|
278
283
|
}
|
|
279
284
|
}
|
|
280
|
-
|
|
281
285
|
/** Kick the peer with the biggest overlap (any round of 2.5sec is isTooMany)
|
|
282
286
|
* - If all peers have the same overlap, kick the one with the most non-public neighbors */
|
|
283
287
|
#improveTopologyByKickingPeers() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NetworkRendererElements, NetworkRendererOptions } from './renderer-options.mjs';
|
|
2
2
|
import { Node, NodesStore, ConnectionsStore } from './renderer-stores.mjs';
|
|
3
|
-
import {
|
|
3
|
+
import { DISCOVERY } from '../core/config.mjs';
|
|
4
4
|
|
|
5
5
|
export class NetworkRenderer {
|
|
6
6
|
initCameraZ = 1400;
|