@hive-p2p/server 1.0.55 → 1.0.57
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/arbiter.mjs +22 -17
- package/core/config.mjs +1 -1
- package/core/gossip.mjs +1 -1
- package/core/node.mjs +2 -2
- package/package.json +1 -1
package/core/arbiter.mjs
CHANGED
|
@@ -6,7 +6,8 @@ import { GOSSIP, UNICAST, LOG_CSS } from './config.mjs';
|
|
|
6
6
|
// Lowered each second by 100ms until 0 (avoid attacker growing balances on multiple disconnected peers)
|
|
7
7
|
|
|
8
8
|
const BYTES_COUNT_PERIOD = 10_000; // 10 seconds
|
|
9
|
-
const
|
|
9
|
+
const MAX_UNICAST_BYTES_PER_PERIOD = 1_000_000; // 1MB per period
|
|
10
|
+
const MAX_GOSSIP_BYTES_PER_PERIOD = 100_000; // 100KB per period
|
|
10
11
|
|
|
11
12
|
const MAX_TRUST = 3_600_000; // +3600 seconds = 1 hour of good behavior
|
|
12
13
|
export const TRUST_VALUES = {
|
|
@@ -37,7 +38,7 @@ export class Arbiter {
|
|
|
37
38
|
* - trustBalance = milliseconds of ban if negative
|
|
38
39
|
* @type {Record<string, number>} */
|
|
39
40
|
trustBalances = {};
|
|
40
|
-
bytesCounters = { gossip:
|
|
41
|
+
bytesCounters = { gossip: {}, unicast: {} };
|
|
41
42
|
bytesCounterResetIn = 0;
|
|
42
43
|
|
|
43
44
|
/** @param {string} selfId @param {import('./crypto-codex.mjs').CryptoCodex} cryptoCodex @param {number} verbose */
|
|
@@ -55,7 +56,7 @@ export class Arbiter {
|
|
|
55
56
|
// RESET GOSSIP BYTES COUNTER
|
|
56
57
|
if (this.bytesCounterResetIn - 1_000 > 0) return;
|
|
57
58
|
this.bytesCounterResetIn = BYTES_COUNT_PERIOD;
|
|
58
|
-
this.bytesCounters = { gossip:
|
|
59
|
+
this.bytesCounters = { gossip: {}, unicast: {} };
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
/** Call from HiveP2P module only!
|
|
@@ -76,10 +77,14 @@ export class Arbiter {
|
|
|
76
77
|
// MESSAGE VERIFICATION
|
|
77
78
|
/** @param {string} peerId @param {number} byteLength @param {'gossip' | 'unicast'} type */
|
|
78
79
|
countMessageBytes(peerId, byteLength, type) {
|
|
79
|
-
if (!this.bytesCounters[type]) this.bytesCounters[type] = 0;
|
|
80
|
-
this.bytesCounters[type] += byteLength;
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
if (!this.bytesCounters[type][peerId]) this.bytesCounters[type][peerId] = 0;
|
|
81
|
+
this.bytesCounters[type][peerId] += byteLength;
|
|
82
|
+
const [maxByte, penality] = type === 'gossip'
|
|
83
|
+
? [MAX_GOSSIP_BYTES_PER_PERIOD, TRUST_VALUES.GOSSIP_FLOOD]
|
|
84
|
+
: [MAX_UNICAST_BYTES_PER_PERIOD, TRUST_VALUES.UNICAST_FLOOD];
|
|
85
|
+
// If under the limit, return true -> else apply penality and return undefined
|
|
86
|
+
if (this.bytesCounters[type][peerId] < maxByte) return true;
|
|
87
|
+
return this.adjustTrust(peerId, penality, `Message ${type} flood detected`);
|
|
83
88
|
}
|
|
84
89
|
/** Call from HiveP2P module only! @param {string} from @param {any} message @param {Uint8Array} serialized @param {number} [powCheckFactor] default: 0.01 (1%) */
|
|
85
90
|
async digestMessage(from, message, serialized, powCheckFactor = .01) {
|
|
@@ -87,19 +92,14 @@ export class Arbiter {
|
|
|
87
92
|
if (!this.#signatureControl(from, message, serialized)) return;
|
|
88
93
|
if (!this.#lengthControl(from, topic ? 'gossip' : 'unicast', serialized, expectedEnd)) return;
|
|
89
94
|
|
|
90
|
-
|
|
91
|
-
|
|
95
|
+
const routeOrHopsOk = topic ? this.#hopsControl(from, message) : this.#routeLengthControl(from, message);
|
|
96
|
+
if (!routeOrHopsOk) return;
|
|
92
97
|
|
|
93
98
|
if (this.isBanished(from) || this.isBanished(senderId)) return;
|
|
94
|
-
if (this.trustBalances[senderId] > TRUST_VALUES.VALID_POW) return;
|
|
99
|
+
if (this.trustBalances[senderId] > TRUST_VALUES.VALID_POW) return true; // we check only low trust balances
|
|
95
100
|
if (Math.random() < powCheckFactor) await this.#powControl(senderId, pubkey);
|
|
96
101
|
return true;
|
|
97
102
|
}
|
|
98
|
-
/** @param {string} from @param {'gossip' | 'unicast'} type */
|
|
99
|
-
#lengthControl(from, type, serialized, expectedEnd) {
|
|
100
|
-
if (!expectedEnd || serialized.length === expectedEnd) return true;
|
|
101
|
-
this.adjustTrust(from, TRUST_VALUES.WRONG_LENGTH, `${type} message length mismatch`);
|
|
102
|
-
}
|
|
103
103
|
/** @param {string} from @param {import('./gossip.mjs').GossipMessage} message @param {Uint8Array} serialized */
|
|
104
104
|
#signatureControl(from, message, serialized) {
|
|
105
105
|
try {
|
|
@@ -115,14 +115,19 @@ export class Arbiter {
|
|
|
115
115
|
}
|
|
116
116
|
this.adjustTrust(from, TRUST_VALUES.WRONG_SIGNATURE, 'Gossip signature invalid');
|
|
117
117
|
}
|
|
118
|
+
/** @param {string} from @param {'gossip' | 'unicast'} type */
|
|
119
|
+
#lengthControl(from, type, serialized, expectedEnd) {
|
|
120
|
+
if (!expectedEnd || serialized.length === expectedEnd) return true;
|
|
121
|
+
this.adjustTrust(from, TRUST_VALUES.WRONG_LENGTH, `${type} message length mismatch`);
|
|
122
|
+
}
|
|
118
123
|
/** GOSSIP only @param {string} from @param {import('./gossip.mjs').GossipMessage} message */
|
|
119
124
|
#hopsControl(from, message) {
|
|
120
|
-
if (message.HOPS <= (GOSSIP.HOPS[message.topic] || GOSSIP.HOPS.default)) return;
|
|
125
|
+
if (message.HOPS <= (GOSSIP.HOPS[message.topic] || GOSSIP.HOPS.default)) return true;
|
|
121
126
|
this.adjustTrust(from, TRUST_VALUES.HOPS_EXCEEDED, 'Gossip HOPS exceeded');
|
|
122
127
|
}
|
|
123
128
|
/** UNICAST only @param {string} from @param {import('./unicast.mjs').DirectMessage} message */
|
|
124
129
|
#routeLengthControl(from, message) {
|
|
125
|
-
if (message.route.length <= UNICAST.MAX_HOPS) return;
|
|
130
|
+
if (message.route.length <= UNICAST.MAX_HOPS) return true;
|
|
126
131
|
this.adjustTrust(from, TRUST_VALUES.HOPS_EXCEEDED, 'Unicast HOPS exceeded');
|
|
127
132
|
}
|
|
128
133
|
/** ONLY APPLY AFTER #signatureControl() - @param {string} senderId @param {Uint8Array} pubkey */
|
package/core/config.mjs
CHANGED
|
@@ -45,7 +45,7 @@ export const SIMULATION = {
|
|
|
45
45
|
|
|
46
46
|
export const NODE = {
|
|
47
47
|
/** 0: none, 1: errors, 2: +important info, 3: +debug, 4: +everything | Can be bypass by some constructors */
|
|
48
|
-
DEFAULT_VERBOSE:
|
|
48
|
+
DEFAULT_VERBOSE: 2,
|
|
49
49
|
/** Timeout for upgrading a "connecting" peer to "connected" | Default: 15_000 (15 seconds) */
|
|
50
50
|
CONNECTION_UPGRADE_TIMEOUT: 15_000,
|
|
51
51
|
/** Flag to indicate if we are running in a browser environment | DON'T MODIFY THIS VALUE */
|
package/core/gossip.mjs
CHANGED
|
@@ -102,7 +102,7 @@ export class Gossip {
|
|
|
102
102
|
else this.callbacks[callbackType].push(callback);
|
|
103
103
|
}
|
|
104
104
|
/** Gossip a message to all connected peers > will be forwarded to all peers
|
|
105
|
-
* @param {string | Uint8Array | Object} data @param {string} topic @param {number} [HOPS] */
|
|
105
|
+
* @param {string | Uint8Array | Object} data @param {string} topic default: 'gossip' @param {number} [HOPS] */
|
|
106
106
|
broadcastToAll(data, topic = 'gossip', HOPS) {
|
|
107
107
|
const hops = HOPS || GOSSIP.HOPS[topic] || GOSSIP.HOPS.default;
|
|
108
108
|
const serializedMessage = this.cryptoCodex.createGossipMessage(topic, data, hops, this.peerStore.neighborsList);
|
package/core/node.mjs
CHANGED
|
@@ -160,7 +160,7 @@ export class Node {
|
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
/** Broadcast a message to all connected peers or to a specified peer
|
|
163
|
-
* @param {string | Uint8Array | Object} data @param {string} topic
|
|
163
|
+
* @param {string | Uint8Array | Object} data @param {string} topic default: 'gossip' @param {string} [targetId] default: broadcast to all
|
|
164
164
|
* @param {number} [timestamp] default: CLOCK.time @param {number} [HOPS] default: GOSSIP.HOPS[topic] || GOSSIP.HOPS.default */
|
|
165
165
|
broadcast(data, topic, HOPS) { this.gossip.broadcastToAll(data, topic, HOPS); }
|
|
166
166
|
|
|
@@ -201,7 +201,7 @@ export class Node {
|
|
|
201
201
|
onMessageData(callback) { this.messager.on('message', callback); }
|
|
202
202
|
|
|
203
203
|
/** Triggered when a new gossip message is received.
|
|
204
|
-
* @param {function} callback can use arguments: (senderId:string,
|
|
204
|
+
* @param {function} callback can use arguments: (senderId:string, data:any, HOPS:number) */
|
|
205
205
|
onGossipData(callback) { this.gossip.on('gossip', callback); }
|
|
206
206
|
|
|
207
207
|
/** Triggered when a new signal offer is received from another peer.
|