@ruvector/edge-net 0.2.0 ā 0.3.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.
- package/dht.js +790 -0
- package/genesis.js +858 -0
- package/p2p.js +577 -0
- package/package.json +24 -3
- package/qdag.js +62 -23
- package/ruvector_edge_net_bg.wasm +0 -0
- package/webrtc.js +37 -5
package/p2p.js
ADDED
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ruvector/edge-net P2P Integration
|
|
3
|
+
*
|
|
4
|
+
* Unified P2P networking layer that integrates:
|
|
5
|
+
* - WebRTC for direct peer connections
|
|
6
|
+
* - DHT for decentralized peer discovery
|
|
7
|
+
* - Signaling for connection establishment
|
|
8
|
+
* - Sync for ledger synchronization
|
|
9
|
+
*
|
|
10
|
+
* @module @ruvector/edge-net/p2p
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { EventEmitter } from 'events';
|
|
14
|
+
import { randomBytes } from 'crypto';
|
|
15
|
+
|
|
16
|
+
// Import P2P components
|
|
17
|
+
import { WebRTCPeerManager, WEBRTC_CONFIG } from './webrtc.js';
|
|
18
|
+
import { DHTNode, createDHTNode } from './dht.js';
|
|
19
|
+
import { SignalingClient } from './signaling.js';
|
|
20
|
+
import { SyncManager } from './sync.js';
|
|
21
|
+
import { QDAG } from './qdag.js';
|
|
22
|
+
import { Ledger } from './ledger.js';
|
|
23
|
+
|
|
24
|
+
// ============================================
|
|
25
|
+
// P2P NETWORK CONFIGURATION
|
|
26
|
+
// ============================================
|
|
27
|
+
|
|
28
|
+
export const P2P_CONFIG = {
|
|
29
|
+
// Auto-start components
|
|
30
|
+
autoStart: {
|
|
31
|
+
signaling: true,
|
|
32
|
+
webrtc: true,
|
|
33
|
+
dht: true,
|
|
34
|
+
sync: true,
|
|
35
|
+
},
|
|
36
|
+
// Connection settings
|
|
37
|
+
maxPeers: 50,
|
|
38
|
+
minPeers: 3,
|
|
39
|
+
// Reconnection
|
|
40
|
+
reconnectInterval: 5000,
|
|
41
|
+
maxReconnectAttempts: 10,
|
|
42
|
+
// DHT settings
|
|
43
|
+
dhtAnnounceInterval: 60000,
|
|
44
|
+
// Sync settings
|
|
45
|
+
syncInterval: 30000,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// ============================================
|
|
49
|
+
// P2P NETWORK NODE
|
|
50
|
+
// ============================================
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Unified P2P Network Node
|
|
54
|
+
*
|
|
55
|
+
* Connects all P2P components into a single interface:
|
|
56
|
+
* - Automatic peer discovery via DHT
|
|
57
|
+
* - WebRTC data channels for direct messaging
|
|
58
|
+
* - QDAG for distributed consensus
|
|
59
|
+
* - Ledger sync across devices
|
|
60
|
+
*/
|
|
61
|
+
export class P2PNetwork extends EventEmitter {
|
|
62
|
+
constructor(identity, options = {}) {
|
|
63
|
+
super();
|
|
64
|
+
this.identity = identity;
|
|
65
|
+
this.options = { ...P2P_CONFIG, ...options };
|
|
66
|
+
|
|
67
|
+
// Node ID
|
|
68
|
+
this.nodeId = identity?.piKey || identity?.nodeId || `node-${randomBytes(8).toString('hex')}`;
|
|
69
|
+
|
|
70
|
+
// Components (initialized on start)
|
|
71
|
+
this.signaling = null;
|
|
72
|
+
this.webrtc = null;
|
|
73
|
+
this.dht = null;
|
|
74
|
+
this.syncManager = null;
|
|
75
|
+
this.qdag = null;
|
|
76
|
+
this.ledger = null;
|
|
77
|
+
|
|
78
|
+
// State
|
|
79
|
+
this.state = 'stopped';
|
|
80
|
+
this.peers = new Map();
|
|
81
|
+
this.stats = {
|
|
82
|
+
startedAt: null,
|
|
83
|
+
peersConnected: 0,
|
|
84
|
+
messagesReceived: 0,
|
|
85
|
+
messagesSent: 0,
|
|
86
|
+
bytesTransferred: 0,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Start the P2P network
|
|
92
|
+
*/
|
|
93
|
+
async start() {
|
|
94
|
+
if (this.state === 'running') return this;
|
|
95
|
+
|
|
96
|
+
console.log('\nš Starting P2P Network...');
|
|
97
|
+
console.log(` Node ID: ${this.nodeId.slice(0, 16)}...`);
|
|
98
|
+
|
|
99
|
+
this.state = 'starting';
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
// Initialize QDAG
|
|
103
|
+
this.qdag = new QDAG({ nodeId: this.nodeId });
|
|
104
|
+
console.log(' ā
QDAG initialized');
|
|
105
|
+
|
|
106
|
+
// Initialize Ledger
|
|
107
|
+
this.ledger = new Ledger({ nodeId: this.nodeId });
|
|
108
|
+
console.log(' ā
Ledger initialized');
|
|
109
|
+
|
|
110
|
+
// Try signaling server first
|
|
111
|
+
if (this.options.autoStart.signaling) {
|
|
112
|
+
await this.startSignaling();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Initialize WebRTC
|
|
116
|
+
if (this.options.autoStart.webrtc) {
|
|
117
|
+
await this.startWebRTC();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Initialize DHT for peer discovery
|
|
121
|
+
if (this.options.autoStart.dht) {
|
|
122
|
+
await this.startDHT();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Initialize sync
|
|
126
|
+
if (this.options.autoStart.sync && this.identity) {
|
|
127
|
+
await this.startSync();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Wire up event handlers
|
|
131
|
+
this.setupEventHandlers();
|
|
132
|
+
|
|
133
|
+
// Announce presence
|
|
134
|
+
await this.announce();
|
|
135
|
+
|
|
136
|
+
this.state = 'running';
|
|
137
|
+
this.stats.startedAt = Date.now();
|
|
138
|
+
|
|
139
|
+
console.log('\nā
P2P Network running');
|
|
140
|
+
console.log(` Mode: ${this.getMode()}`);
|
|
141
|
+
console.log(` Peers: ${this.peers.size}`);
|
|
142
|
+
|
|
143
|
+
this.emit('started', { nodeId: this.nodeId, mode: this.getMode() });
|
|
144
|
+
|
|
145
|
+
return this;
|
|
146
|
+
|
|
147
|
+
} catch (error) {
|
|
148
|
+
this.state = 'error';
|
|
149
|
+
console.error('ā P2P Network start failed:', error.message);
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Stop the P2P network
|
|
156
|
+
*/
|
|
157
|
+
async stop() {
|
|
158
|
+
console.log('\nš Stopping P2P Network...');
|
|
159
|
+
|
|
160
|
+
this.state = 'stopping';
|
|
161
|
+
|
|
162
|
+
if (this.syncManager) this.syncManager.stop();
|
|
163
|
+
if (this.dht) this.dht.stop();
|
|
164
|
+
if (this.webrtc) this.webrtc.close();
|
|
165
|
+
if (this.signaling) this.signaling.disconnect();
|
|
166
|
+
|
|
167
|
+
this.peers.clear();
|
|
168
|
+
this.state = 'stopped';
|
|
169
|
+
|
|
170
|
+
this.emit('stopped');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Start signaling client
|
|
175
|
+
*/
|
|
176
|
+
async startSignaling() {
|
|
177
|
+
try {
|
|
178
|
+
this.signaling = new SignalingClient({
|
|
179
|
+
serverUrl: this.options.signalingUrl || WEBRTC_CONFIG.signalingServers[0],
|
|
180
|
+
peerId: this.nodeId,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const connected = await this.signaling.connect();
|
|
184
|
+
if (connected) {
|
|
185
|
+
console.log(' ā
Signaling connected');
|
|
186
|
+
} else {
|
|
187
|
+
console.log(' ā ļø Signaling unavailable (will use DHT)');
|
|
188
|
+
}
|
|
189
|
+
} catch (err) {
|
|
190
|
+
console.log(' ā ļø Signaling failed:', err.message);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Start WebRTC peer manager
|
|
196
|
+
*/
|
|
197
|
+
async startWebRTC() {
|
|
198
|
+
try {
|
|
199
|
+
this.webrtc = new WebRTCPeerManager({
|
|
200
|
+
piKey: this.nodeId,
|
|
201
|
+
siteId: this.identity?.siteId || 'edge-net',
|
|
202
|
+
}, this.options.webrtc || {});
|
|
203
|
+
|
|
204
|
+
await this.webrtc.initialize();
|
|
205
|
+
console.log(` ā
WebRTC initialized (${this.webrtc.mode} mode)`);
|
|
206
|
+
} catch (err) {
|
|
207
|
+
console.log(' ā ļø WebRTC failed:', err.message);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Start DHT for peer discovery
|
|
213
|
+
*/
|
|
214
|
+
async startDHT() {
|
|
215
|
+
try {
|
|
216
|
+
if (this.webrtc) {
|
|
217
|
+
this.dht = await createDHTNode(this.webrtc, {
|
|
218
|
+
id: this.nodeId,
|
|
219
|
+
});
|
|
220
|
+
} else {
|
|
221
|
+
this.dht = new DHTNode({ id: this.nodeId });
|
|
222
|
+
await this.dht.start();
|
|
223
|
+
}
|
|
224
|
+
console.log(' ā
DHT initialized');
|
|
225
|
+
} catch (err) {
|
|
226
|
+
console.log(' ā ļø DHT failed:', err.message);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Start sync manager
|
|
232
|
+
*/
|
|
233
|
+
async startSync() {
|
|
234
|
+
try {
|
|
235
|
+
this.syncManager = new SyncManager(this.identity, this.ledger, {
|
|
236
|
+
enableP2P: !!this.webrtc,
|
|
237
|
+
enableFirestore: false, // Use P2P only for now
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
await this.syncManager.start();
|
|
241
|
+
console.log(' ā
Sync initialized');
|
|
242
|
+
} catch (err) {
|
|
243
|
+
console.log(' ā ļø Sync failed:', err.message);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Setup event handlers between components
|
|
249
|
+
*/
|
|
250
|
+
setupEventHandlers() {
|
|
251
|
+
// WebRTC events
|
|
252
|
+
if (this.webrtc) {
|
|
253
|
+
this.webrtc.on('peer-connected', (peerId) => {
|
|
254
|
+
this.handlePeerConnected(peerId);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
this.webrtc.on('peer-disconnected', (peerId) => {
|
|
258
|
+
this.handlePeerDisconnected(peerId);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
this.webrtc.on('message', ({ from, message }) => {
|
|
262
|
+
this.handleMessage(from, message);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Signaling events
|
|
267
|
+
if (this.signaling) {
|
|
268
|
+
this.signaling.on('peer-joined', ({ peerId }) => {
|
|
269
|
+
this.connectToPeer(peerId);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
this.signaling.on('offer', async ({ from, offer }) => {
|
|
273
|
+
if (this.webrtc) {
|
|
274
|
+
await this.webrtc.handleOffer({ from, offer });
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
this.signaling.on('answer', async ({ from, answer }) => {
|
|
279
|
+
if (this.webrtc) {
|
|
280
|
+
await this.webrtc.handleAnswer({ from, answer });
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
this.signaling.on('ice-candidate', async ({ from, candidate }) => {
|
|
285
|
+
if (this.webrtc) {
|
|
286
|
+
await this.webrtc.handleIceCandidate({ from, candidate });
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// DHT events
|
|
292
|
+
if (this.dht) {
|
|
293
|
+
this.dht.on('peer-added', (peer) => {
|
|
294
|
+
this.connectToPeer(peer.id);
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Sync events
|
|
299
|
+
if (this.syncManager) {
|
|
300
|
+
this.syncManager.on('synced', (data) => {
|
|
301
|
+
this.emit('synced', data);
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// QDAG events
|
|
306
|
+
if (this.qdag) {
|
|
307
|
+
this.qdag.on('transaction', (tx) => {
|
|
308
|
+
this.broadcastTransaction(tx);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
this.qdag.on('confirmed', (tx) => {
|
|
312
|
+
this.emit('transaction-confirmed', tx);
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Announce presence to the network
|
|
319
|
+
*/
|
|
320
|
+
async announce() {
|
|
321
|
+
// Announce via signaling
|
|
322
|
+
if (this.signaling?.isConnected) {
|
|
323
|
+
this.signaling.send({
|
|
324
|
+
type: 'announce',
|
|
325
|
+
piKey: this.nodeId,
|
|
326
|
+
siteId: this.identity?.siteId,
|
|
327
|
+
capabilities: ['compute', 'storage', 'verify'],
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Announce via DHT
|
|
332
|
+
if (this.dht) {
|
|
333
|
+
await this.dht.announce('edge-net');
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Connect to a peer
|
|
339
|
+
*/
|
|
340
|
+
async connectToPeer(peerId) {
|
|
341
|
+
if (peerId === this.nodeId) return;
|
|
342
|
+
if (this.peers.has(peerId)) return;
|
|
343
|
+
|
|
344
|
+
try {
|
|
345
|
+
if (this.webrtc) {
|
|
346
|
+
await this.webrtc.connectToPeer(peerId);
|
|
347
|
+
}
|
|
348
|
+
} catch (err) {
|
|
349
|
+
console.warn(`[P2P] Failed to connect to ${peerId.slice(0, 8)}:`, err.message);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Handle peer connected
|
|
355
|
+
*/
|
|
356
|
+
handlePeerConnected(peerId) {
|
|
357
|
+
this.peers.set(peerId, {
|
|
358
|
+
id: peerId,
|
|
359
|
+
connectedAt: Date.now(),
|
|
360
|
+
lastSeen: Date.now(),
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
this.stats.peersConnected++;
|
|
364
|
+
|
|
365
|
+
// Register with sync
|
|
366
|
+
if (this.syncManager && this.webrtc) {
|
|
367
|
+
const peer = this.webrtc.peers.get(peerId);
|
|
368
|
+
if (peer?.dataChannel) {
|
|
369
|
+
this.syncManager.registerPeer(peerId, peer.dataChannel);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
this.emit('peer-connected', { peerId });
|
|
374
|
+
console.log(` š Connected to peer: ${peerId.slice(0, 8)}... (${this.peers.size} total)`);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Handle peer disconnected
|
|
379
|
+
*/
|
|
380
|
+
handlePeerDisconnected(peerId) {
|
|
381
|
+
this.peers.delete(peerId);
|
|
382
|
+
this.emit('peer-disconnected', { peerId });
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Handle incoming message
|
|
387
|
+
*/
|
|
388
|
+
handleMessage(from, message) {
|
|
389
|
+
this.stats.messagesReceived++;
|
|
390
|
+
|
|
391
|
+
// Route by message type
|
|
392
|
+
switch (message.type) {
|
|
393
|
+
case 'qdag_transaction':
|
|
394
|
+
this.handleQDAGTransaction(from, message);
|
|
395
|
+
break;
|
|
396
|
+
|
|
397
|
+
case 'qdag_sync_request':
|
|
398
|
+
this.handleQDAGSyncRequest(from, message);
|
|
399
|
+
break;
|
|
400
|
+
|
|
401
|
+
case 'qdag_sync_response':
|
|
402
|
+
this.handleQDAGSyncResponse(from, message);
|
|
403
|
+
break;
|
|
404
|
+
|
|
405
|
+
default:
|
|
406
|
+
this.emit('message', { from, message });
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Handle QDAG transaction
|
|
412
|
+
*/
|
|
413
|
+
handleQDAGTransaction(from, message) {
|
|
414
|
+
if (message.transaction && this.qdag) {
|
|
415
|
+
try {
|
|
416
|
+
this.qdag.addTransaction(message.transaction);
|
|
417
|
+
} catch (err) {
|
|
418
|
+
// Duplicate or invalid transaction
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Handle QDAG sync request
|
|
425
|
+
*/
|
|
426
|
+
handleQDAGSyncRequest(from, message) {
|
|
427
|
+
if (this.qdag && this.webrtc) {
|
|
428
|
+
const transactions = this.qdag.export(message.since || 0);
|
|
429
|
+
this.webrtc.sendToPeer(from, {
|
|
430
|
+
type: 'qdag_sync_response',
|
|
431
|
+
transactions: transactions.transactions,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Handle QDAG sync response
|
|
438
|
+
*/
|
|
439
|
+
handleQDAGSyncResponse(from, message) {
|
|
440
|
+
if (message.transactions && this.qdag) {
|
|
441
|
+
this.qdag.import({ transactions: message.transactions });
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Broadcast a transaction to all peers
|
|
447
|
+
*/
|
|
448
|
+
broadcastTransaction(tx) {
|
|
449
|
+
if (this.webrtc) {
|
|
450
|
+
this.webrtc.broadcast({
|
|
451
|
+
type: 'qdag_transaction',
|
|
452
|
+
transaction: tx.toJSON(),
|
|
453
|
+
});
|
|
454
|
+
this.stats.messagesSent++;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Send message to a specific peer
|
|
460
|
+
*/
|
|
461
|
+
sendToPeer(peerId, message) {
|
|
462
|
+
if (this.webrtc) {
|
|
463
|
+
const sent = this.webrtc.sendToPeer(peerId, message);
|
|
464
|
+
if (sent) this.stats.messagesSent++;
|
|
465
|
+
return sent;
|
|
466
|
+
}
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Broadcast message to all peers
|
|
472
|
+
*/
|
|
473
|
+
broadcast(message) {
|
|
474
|
+
if (this.webrtc) {
|
|
475
|
+
const sent = this.webrtc.broadcast(message);
|
|
476
|
+
this.stats.messagesSent += sent;
|
|
477
|
+
return sent;
|
|
478
|
+
}
|
|
479
|
+
return 0;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Submit a task to the network
|
|
484
|
+
*/
|
|
485
|
+
async submitTask(task) {
|
|
486
|
+
if (!this.qdag) throw new Error('QDAG not initialized');
|
|
487
|
+
|
|
488
|
+
const tx = this.qdag.createTransaction('task', {
|
|
489
|
+
taskId: task.id || randomBytes(8).toString('hex'),
|
|
490
|
+
type: task.type,
|
|
491
|
+
data: task.data,
|
|
492
|
+
reward: task.reward || 0,
|
|
493
|
+
priority: task.priority || 'medium',
|
|
494
|
+
}, {
|
|
495
|
+
issuer: this.nodeId,
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
return tx;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Credit the ledger
|
|
503
|
+
*/
|
|
504
|
+
credit(amount, reason) {
|
|
505
|
+
if (!this.ledger) throw new Error('Ledger not initialized');
|
|
506
|
+
this.ledger.credit(amount, reason);
|
|
507
|
+
|
|
508
|
+
// Trigger sync
|
|
509
|
+
if (this.syncManager) {
|
|
510
|
+
this.syncManager.sync();
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Get current balance
|
|
516
|
+
*/
|
|
517
|
+
getBalance() {
|
|
518
|
+
return this.ledger?.getBalance() || 0;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Get connection mode
|
|
523
|
+
*/
|
|
524
|
+
getMode() {
|
|
525
|
+
if (this.webrtc?.mode === 'webrtc' && this.signaling?.isConnected) {
|
|
526
|
+
return 'full-p2p';
|
|
527
|
+
}
|
|
528
|
+
if (this.webrtc?.mode === 'webrtc') {
|
|
529
|
+
return 'webrtc-dht';
|
|
530
|
+
}
|
|
531
|
+
if (this.dht) {
|
|
532
|
+
return 'dht-only';
|
|
533
|
+
}
|
|
534
|
+
return 'local';
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Get network statistics
|
|
539
|
+
*/
|
|
540
|
+
getStats() {
|
|
541
|
+
return {
|
|
542
|
+
...this.stats,
|
|
543
|
+
nodeId: this.nodeId,
|
|
544
|
+
state: this.state,
|
|
545
|
+
mode: this.getMode(),
|
|
546
|
+
peers: this.peers.size,
|
|
547
|
+
signalingConnected: this.signaling?.isConnected || false,
|
|
548
|
+
webrtcMode: this.webrtc?.mode || 'none',
|
|
549
|
+
dhtPeers: this.dht?.getPeers().length || 0,
|
|
550
|
+
qdagTransactions: this.qdag?.transactions.size || 0,
|
|
551
|
+
ledgerBalance: this.getBalance(),
|
|
552
|
+
uptime: this.stats.startedAt ? Date.now() - this.stats.startedAt : 0,
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Get peer list
|
|
558
|
+
*/
|
|
559
|
+
getPeers() {
|
|
560
|
+
return Array.from(this.peers.values());
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Create and start a P2P network node
|
|
566
|
+
*/
|
|
567
|
+
export async function createP2PNetwork(identity, options = {}) {
|
|
568
|
+
const network = new P2PNetwork(identity, options);
|
|
569
|
+
await network.start();
|
|
570
|
+
return network;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// ============================================
|
|
574
|
+
// EXPORTS
|
|
575
|
+
// ============================================
|
|
576
|
+
|
|
577
|
+
export default P2PNetwork;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ruvector/edge-net",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Distributed compute intelligence network with AI agents and workers - contribute browser compute, spawn distributed AI agents, earn credits. Features Time Crystal coordination, Neural DAG attention, P2P swarm intelligence, ONNX inference, WebRTC signaling, CRDT ledger, and multi-agent workflows.",
|
|
6
6
|
"main": "ruvector_edge_net.js",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"bin": {
|
|
10
10
|
"edge-net": "./cli.js",
|
|
11
11
|
"ruvector-edge": "./cli.js",
|
|
12
|
-
"edge-net-join": "./join.js"
|
|
12
|
+
"edge-net-join": "./join.js",
|
|
13
|
+
"edge-net-genesis": "./genesis.js"
|
|
13
14
|
},
|
|
14
15
|
"keywords": [
|
|
15
16
|
"wasm",
|
|
@@ -43,7 +44,12 @@
|
|
|
43
44
|
"signaling",
|
|
44
45
|
"scheduler",
|
|
45
46
|
"monitoring",
|
|
46
|
-
"qdag"
|
|
47
|
+
"qdag",
|
|
48
|
+
"dht",
|
|
49
|
+
"kademlia",
|
|
50
|
+
"genesis-node",
|
|
51
|
+
"turn-server",
|
|
52
|
+
"nat-traversal"
|
|
47
53
|
],
|
|
48
54
|
"author": "RuVector Team <team@ruvector.dev>",
|
|
49
55
|
"license": "MIT",
|
|
@@ -79,6 +85,9 @@
|
|
|
79
85
|
"ledger.js",
|
|
80
86
|
"scheduler.js",
|
|
81
87
|
"monitor.js",
|
|
88
|
+
"dht.js",
|
|
89
|
+
"genesis.js",
|
|
90
|
+
"p2p.js",
|
|
82
91
|
"README.md",
|
|
83
92
|
"LICENSE"
|
|
84
93
|
],
|
|
@@ -128,6 +137,15 @@
|
|
|
128
137
|
},
|
|
129
138
|
"./monitor": {
|
|
130
139
|
"import": "./monitor.js"
|
|
140
|
+
},
|
|
141
|
+
"./dht": {
|
|
142
|
+
"import": "./dht.js"
|
|
143
|
+
},
|
|
144
|
+
"./genesis": {
|
|
145
|
+
"import": "./genesis.js"
|
|
146
|
+
},
|
|
147
|
+
"./p2p": {
|
|
148
|
+
"import": "./p2p.js"
|
|
131
149
|
}
|
|
132
150
|
},
|
|
133
151
|
"sideEffects": [
|
|
@@ -147,6 +165,9 @@
|
|
|
147
165
|
"peers": "node join.js --peers",
|
|
148
166
|
"history": "node join.js --history",
|
|
149
167
|
"signaling": "node -e \"import('./signaling.js').then(m => new m.SignalingServer().start())\"",
|
|
168
|
+
"genesis": "node genesis.js",
|
|
169
|
+
"genesis:start": "node genesis.js --port 8787",
|
|
170
|
+
"p2p": "node -e \"import('./p2p.js').then(m => m.createP2PNetwork({ nodeId: 'test' }))\"",
|
|
150
171
|
"monitor": "node -e \"import('./monitor.js').then(m => { const mon = new m.Monitor(); mon.start(); setInterval(() => console.log(JSON.stringify(mon.generateReport(), null, 2)), 5000); })\""
|
|
151
172
|
},
|
|
152
173
|
"dependencies": {
|