@hive-p2p/server 1.0.65 → 1.0.67
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/crypto-codex.mjs +11 -11
- package/core/gossip.mjs +2 -2
- package/core/node-services.mjs +4 -2
- package/core/node.mjs +2 -2
- package/core/topologist.mjs +4 -3
- package/core/unicast.mjs +7 -4
- package/package.json +1 -1
- package/services/cryptos.mjs +9 -4
package/core/crypto-codex.mjs
CHANGED
|
@@ -105,7 +105,7 @@ export class CryptoCodex {
|
|
|
105
105
|
async #generateAntiSybilIdentity(seed, asPublicNode) {
|
|
106
106
|
const maxIterations = (2 ** IDENTITY.DIFFICULTY) * 100; // avoid infinite loop
|
|
107
107
|
for (let i = 0; i < maxIterations; i++) { // avoid infinite loop
|
|
108
|
-
const { secretKey, publicKey } = ed25519.
|
|
108
|
+
const { secretKey, publicKey } = await ed25519.keygenAsync(seed);
|
|
109
109
|
const id = this.#idFromPublicKey(publicKey);
|
|
110
110
|
if (asPublicNode && !this.isPublicNode(id)) continue; // Check prefix
|
|
111
111
|
if (!asPublicNode && this.isPublicNode(id)) continue; // Check prefix
|
|
@@ -120,13 +120,15 @@ export class CryptoCodex {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
// MESSSAGE CREATION (SERIALIZATION AND SIGNATURE INCLUDED)
|
|
123
|
-
|
|
123
|
+
/** @param {Uint8Array} bufferView @param {Uint8Array} privateKey @param {number} [signaturePosition] */
|
|
124
|
+
async signBufferViewAndAppendSignature(bufferView, privateKey, signaturePosition = bufferView.length - IDENTITY.SIGNATURE_LENGTH) {
|
|
124
125
|
if (this.AVOID_CRYPTO) return;
|
|
125
126
|
const dataToSign = bufferView.subarray(0, signaturePosition);
|
|
126
|
-
|
|
127
|
+
const signature = await ed25519.signAsync(dataToSign, privateKey);
|
|
128
|
+
bufferView.set(signature, signaturePosition);
|
|
127
129
|
}
|
|
128
130
|
/** @param {string} topic @param {string | Uint8Array | Object} data @param {number} [HOPS] @param {string[]} route @param {string[]} [neighbors] */
|
|
129
|
-
createGossipMessage(topic, data, HOPS = 3, neighbors = [], timestamp = CLOCK.time) {
|
|
131
|
+
async createGossipMessage(topic, data, HOPS = 3, neighbors = [], timestamp = CLOCK.time) {
|
|
130
132
|
const MARKER = GOSSIP.MARKERS_BYTES[topic];
|
|
131
133
|
if (MARKER === undefined) throw new Error(`Failed to create gossip message: unknown topic '${topic}'.`);
|
|
132
134
|
|
|
@@ -140,7 +142,7 @@ export class CryptoCodex {
|
|
|
140
142
|
bufferView.set(neighborsBytes, 47); // X bytes for neighbors
|
|
141
143
|
bufferView.set(dataBytes, 47 + neighborsBytes.length); // X bytes for data
|
|
142
144
|
bufferView.set([Math.min(255, HOPS)], totalBytes - 1); // 1 byte for HOPS (Unsigned)
|
|
143
|
-
this.signBufferViewAndAppendSignature(bufferView, this.privateKey, totalBytes - IDENTITY.SIGNATURE_LENGTH - 1);
|
|
145
|
+
await this.signBufferViewAndAppendSignature(bufferView, this.privateKey, totalBytes - IDENTITY.SIGNATURE_LENGTH - 1);
|
|
144
146
|
return bufferView;
|
|
145
147
|
}
|
|
146
148
|
/** @param {Uint8Array} serializedMessage */
|
|
@@ -151,7 +153,7 @@ export class CryptoCodex {
|
|
|
151
153
|
return clone;
|
|
152
154
|
}
|
|
153
155
|
/** @param {string} type @param {string | Uint8Array | Object} data @param {string[]} route @param {string[]} [neighbors] */
|
|
154
|
-
createUnicastMessage(type, data, route, neighbors = [], timestamp = CLOCK.time) {
|
|
156
|
+
async createUnicastMessage(type, data, route, neighbors = [], timestamp = CLOCK.time) {
|
|
155
157
|
const MARKER = UNICAST.MARKERS_BYTES[type];
|
|
156
158
|
if (MARKER === undefined) throw new Error(`Failed to create unicast message: unknown type '${type}'.`);
|
|
157
159
|
if (route.length < 2) throw new Error('Failed to create unicast message: route must have at least 2 nodes (next hop and target).');
|
|
@@ -171,11 +173,11 @@ export class CryptoCodex {
|
|
|
171
173
|
bufferView.set([route.length], 47 + NDBL); // 1 byte for route length
|
|
172
174
|
bufferView.set(routeBytes, 47 + 1 + NDBL); // X bytes for route
|
|
173
175
|
|
|
174
|
-
this.signBufferViewAndAppendSignature(bufferView, this.privateKey, totalBytes - IDENTITY.SIGNATURE_LENGTH);
|
|
176
|
+
await this.signBufferViewAndAppendSignature(bufferView, this.privateKey, totalBytes - IDENTITY.SIGNATURE_LENGTH);
|
|
175
177
|
return bufferView;
|
|
176
178
|
}
|
|
177
179
|
/** @param {Uint8Array} serialized @param {string[]} newRoute */
|
|
178
|
-
createReroutedUnicastMessage(serialized, newRoute) {
|
|
180
|
+
async createReroutedUnicastMessage(serialized, newRoute) {
|
|
179
181
|
if (newRoute.length < 2) throw new Error('Failed to create rerouted unicast message: route must have at least 2 nodes (next hop and target).');
|
|
180
182
|
if (newRoute.length > UNICAST.MAX_HOPS) throw new Error(`Failed to create rerouted unicast message: route exceeds max hops (${UNICAST.MAX_HOPS}).`);
|
|
181
183
|
|
|
@@ -187,7 +189,7 @@ export class CryptoCodex {
|
|
|
187
189
|
bufferView.set(this.publicKey, serialized.length); // 32 bytes for new public key
|
|
188
190
|
for (let i = 0; i < routeBytesArray.length; i++) bufferView.set(routeBytesArray[i], serialized.length + 32 + (i * IDENTITY.ID_LENGTH)); // new route
|
|
189
191
|
|
|
190
|
-
this.signBufferViewAndAppendSignature(bufferView, this.privateKey, totalBytes - IDENTITY.SIGNATURE_LENGTH);
|
|
192
|
+
await this.signBufferViewAndAppendSignature(bufferView, this.privateKey, totalBytes - IDENTITY.SIGNATURE_LENGTH);
|
|
191
193
|
return bufferView;
|
|
192
194
|
}
|
|
193
195
|
/** @param {string[]} ids */
|
|
@@ -217,8 +219,6 @@ export class CryptoCodex {
|
|
|
217
219
|
/** @param {Uint8Array} publicKey @param {Uint8Array} dataToVerify @param {Uint8Array} signature */
|
|
218
220
|
async verifySignature(publicKey, dataToVerify, signature) {
|
|
219
221
|
if (this.AVOID_CRYPTO) return true;
|
|
220
|
-
//return ed25519.verifyAsync(signature, dataToVerify, publicKey);
|
|
221
|
-
// new version with batching to optimize timings using ed25519 async.
|
|
222
222
|
return this.verifier.verifySignature(publicKey, dataToVerify, signature);
|
|
223
223
|
}
|
|
224
224
|
/** @param {Uint8Array} bufferView */
|
package/core/gossip.mjs
CHANGED
|
@@ -103,9 +103,9 @@ export class Gossip {
|
|
|
103
103
|
}
|
|
104
104
|
/** Gossip a message to all connected peers > will be forwarded to all peers
|
|
105
105
|
* @param {string | Uint8Array | Object} data @param {string} topic default: 'gossip' @param {number} [HOPS] */
|
|
106
|
-
broadcastToAll(data, topic = 'gossip', HOPS) {
|
|
106
|
+
async broadcastToAll(data, topic = 'gossip', HOPS) {
|
|
107
107
|
const hops = HOPS || GOSSIP.HOPS[topic] || GOSSIP.HOPS.default;
|
|
108
|
-
const serializedMessage = this.cryptoCodex.createGossipMessage(topic, data, hops, this.peerStore.neighborsList);
|
|
108
|
+
const serializedMessage = await this.cryptoCodex.createGossipMessage(topic, data, hops, this.peerStore.neighborsList);
|
|
109
109
|
if (!this.bloomFilter.addMessage(serializedMessage)) return; // avoid sending duplicate messages
|
|
110
110
|
if (this.verbose > 3) console.log(`(${this.id}) Gossip ${topic}, to ${JSON.stringify(this.peerStore.neighborsList)}: ${data}`);
|
|
111
111
|
for (const peerId of this.peerStore.neighborsList) this.#broadcastToPeer(peerId, serializedMessage);
|
package/core/node-services.mjs
CHANGED
|
@@ -51,7 +51,7 @@ export class NodeServices {
|
|
|
51
51
|
#startWebSocketServer(domain = 'localhost', port = SERVICE.PORT) {
|
|
52
52
|
this.wsServer = new TRANSPORTS.WS_SERVER({ port, host: domain });
|
|
53
53
|
this.wsServer.on('error', (error) => console.error(`WebSocket error on Node #${this.id}:`, error));
|
|
54
|
-
this.wsServer.on('connection', (ws) => {
|
|
54
|
+
this.wsServer.on('connection', async (ws) => {
|
|
55
55
|
ws.on('close', () => { if (remoteId) for (const cb of this.peerStore.callbacks.disconnect) cb(remoteId, 'in'); });
|
|
56
56
|
ws.on('error', (error) => console.error(`WebSocket error on Node #${this.id} with peer ${remoteId}:`, error.stack));
|
|
57
57
|
|
|
@@ -78,7 +78,9 @@ export class NodeServices {
|
|
|
78
78
|
for (const cb of this.peerStore.callbacks.connect) cb(remoteId, 'in');
|
|
79
79
|
}
|
|
80
80
|
});
|
|
81
|
-
|
|
81
|
+
|
|
82
|
+
const handshakeMsg = await this.cryptoCodex.createUnicastMessage('handshake', null, [this.id, this.id], this.peerStore.neighborsList);
|
|
83
|
+
ws.send(handshakeMsg);
|
|
82
84
|
});
|
|
83
85
|
}
|
|
84
86
|
#startSTUNServer(host = 'localhost', port = SERVICE.PORT + 1) {
|
package/core/node.mjs
CHANGED
|
@@ -162,10 +162,10 @@ export class Node {
|
|
|
162
162
|
/** Broadcast a message to all connected peers or to a specified peer
|
|
163
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
|
-
broadcast(data, topic, HOPS) { this.gossip.broadcastToAll(data, topic, HOPS); }
|
|
165
|
+
async broadcast(data, topic, HOPS) { return this.gossip.broadcastToAll(data, topic, HOPS); }
|
|
166
166
|
|
|
167
167
|
/** @param {string} remoteId @param {string | Uint8Array | Object} data @param {string} type */
|
|
168
|
-
sendMessage(remoteId, data, type, spread = 1) { this.messager.sendUnicast(remoteId, data, type, spread); }
|
|
168
|
+
async sendMessage(remoteId, data, type, spread = 1) { return this.messager.sendUnicast(remoteId, data, type, spread); }
|
|
169
169
|
|
|
170
170
|
/** Send a connection request to a peer */
|
|
171
171
|
async tryConnectToPeer(targetId = 'toto', retry = 10) {
|
package/core/topologist.mjs
CHANGED
|
@@ -169,7 +169,7 @@ export class Topologist {
|
|
|
169
169
|
let remoteId = null;
|
|
170
170
|
const ws = new TRANSPORTS.WS_CLIENT(this.#getFullWsUrl(publicUrl)); ws.binaryType = 'arraybuffer';
|
|
171
171
|
ws.onerror = (error) => console.error(`WebSocket error:`, error.stack);
|
|
172
|
-
ws.onopen = () => {
|
|
172
|
+
ws.onopen = async () => {
|
|
173
173
|
this.bootstrapsConnectionState.set(publicUrl, true);
|
|
174
174
|
ws.onclose = () => {
|
|
175
175
|
this.bootstrapsConnectionState.set(publicUrl, false);
|
|
@@ -197,8 +197,9 @@ export class Topologist {
|
|
|
197
197
|
for (const cb of this.peerStore.callbacks.connect) cb(remoteId, 'out');
|
|
198
198
|
}
|
|
199
199
|
};
|
|
200
|
-
|
|
201
|
-
|
|
200
|
+
const handshakeMsg = await this.cryptoCodex.createUnicastMessage('handshake', null, [this.id, this.id], this.peerStore.neighborsList);
|
|
201
|
+
ws.send(handshakeMsg);
|
|
202
|
+
};
|
|
202
203
|
}
|
|
203
204
|
#tryToSpreadSDP(nonPublicNeighborsCount = 0, isHalfReached = false) { // LOOP TO SELECT ONE UNSEND READY OFFER AND BROADCAST IT
|
|
204
205
|
if (!this.automation.spreadOffers) return;
|
package/core/unicast.mjs
CHANGED
|
@@ -76,13 +76,14 @@ export class UnicastMessager {
|
|
|
76
76
|
/** Send unicast message to a target
|
|
77
77
|
* @param {string} remoteId @param {string | Uint8Array | Object} data @param {string} type
|
|
78
78
|
* @param {number} [spread] Max neighbors used to relay the message, default: 1 */
|
|
79
|
-
sendUnicast(remoteId, data, type = 'message', spread = 1) {
|
|
79
|
+
async sendUnicast(remoteId, data, type = 'message', spread = 1) {
|
|
80
80
|
if (remoteId === this.id) return false;
|
|
81
81
|
|
|
82
82
|
const builtResult = this.pathFinder.buildRoutes(remoteId, this.maxRoutes, this.maxHops, this.maxNodes, true);
|
|
83
83
|
if (!builtResult.success) return false;
|
|
84
84
|
|
|
85
85
|
// Caution: re-routing usage who can involve insane results
|
|
86
|
+
const createdMessagePromises = [];
|
|
86
87
|
const finalSpread = builtResult.success === 'blind' ? 1 : spread; // Spread only if re-routing is false
|
|
87
88
|
for (let i = 0; i < Math.min(finalSpread, builtResult.routes.length); i++) {
|
|
88
89
|
const route = builtResult.routes[i].path;
|
|
@@ -90,9 +91,11 @@ export class UnicastMessager {
|
|
|
90
91
|
if (this.verbose > 1) console.warn(`Cannot send unicast message to ${remoteId} as route exceeds maxHops (${UNICAST.MAX_HOPS}). BFS incurred.`);
|
|
91
92
|
continue; // too long route
|
|
92
93
|
}
|
|
93
|
-
|
|
94
|
-
this.#sendMessageToPeer(route[1], message); // send to next peer
|
|
94
|
+
createdMessagePromises.push(this.cryptoCodex.createUnicastMessage(type, data, route, this.peerStore.neighborsList));
|
|
95
95
|
}
|
|
96
|
+
|
|
97
|
+
const createdMessages = await Promise.all(createdMessagePromises);
|
|
98
|
+
for (const message of createdMessages) this.#sendMessageToPeer(message.route[1], message);
|
|
96
99
|
return true;
|
|
97
100
|
}
|
|
98
101
|
/** @param {string} targetId @param {Uint8Array} serialized */
|
|
@@ -148,7 +151,7 @@ export class UnicastMessager {
|
|
|
148
151
|
return; // too long route
|
|
149
152
|
}
|
|
150
153
|
|
|
151
|
-
const patchedMessage = this.cryptoCodex.createReroutedUnicastMessage(serialized, newRoute);
|
|
154
|
+
const patchedMessage = await this.cryptoCodex.createReroutedUnicastMessage(serialized, newRoute);
|
|
152
155
|
const nextPeerId = newRoute[selfPosition + 1];
|
|
153
156
|
this.#sendMessageToPeer(nextPeerId, patchedMessage);
|
|
154
157
|
}
|
package/package.json
CHANGED
package/services/cryptos.mjs
CHANGED
|
@@ -2,14 +2,19 @@ import { Converter } from './converter.mjs';
|
|
|
2
2
|
const IS_BROWSER = typeof window !== 'undefined';
|
|
3
3
|
|
|
4
4
|
// ED25519 EXPOSURE NODEJS/BROWSER COMPATIBLE ---------------------------------
|
|
5
|
-
const [ed_, {sha512}] = await Promise.all([
|
|
5
|
+
/*const [ed_, {sha512}] = await Promise.all([
|
|
6
6
|
import(IS_BROWSER ? '../libs/ed25519-custom.min.js' : '@noble/ed25519'),
|
|
7
7
|
import(IS_BROWSER ? '../libs/ed25519-custom.min.js' : '@noble/hashes/sha2.js')
|
|
8
|
-
])
|
|
8
|
+
]);*/
|
|
9
|
+
|
|
10
|
+
// Now we only use nodejs version without sha512
|
|
11
|
+
const ed_ = await import('@noble/ed25519');
|
|
12
|
+
|
|
9
13
|
/** @type {import('@noble/ed25519')} */
|
|
10
14
|
const ed25519 = ed_.default || ed_;
|
|
11
|
-
ed25519.hashes.sha512 = sha512;
|
|
12
|
-
export { ed25519, sha512 };
|
|
15
|
+
//ed25519.hashes.sha512 = sha512;
|
|
16
|
+
//export { ed25519, sha512 };
|
|
17
|
+
export { ed25519 };
|
|
13
18
|
|
|
14
19
|
//-----------------------------------------------------------------------------
|
|
15
20
|
|