@ruvector/edge-net 0.2.0 → 0.2.1
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/package.json +1 -1
- package/qdag.js +62 -23
- package/ruvector_edge_net_bg.wasm +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ruvector/edge-net",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
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",
|
package/qdag.js
CHANGED
|
@@ -193,26 +193,37 @@ export class QDAG extends EventEmitter {
|
|
|
193
193
|
* Select tips for new transaction (weighted random walk)
|
|
194
194
|
*/
|
|
195
195
|
selectTips(count = 2) {
|
|
196
|
+
// Ensure genesis exists
|
|
197
|
+
if (!this.transactions.has('genesis')) {
|
|
198
|
+
this.createGenesis();
|
|
199
|
+
}
|
|
200
|
+
|
|
196
201
|
const tips = Array.from(this.tips);
|
|
197
202
|
|
|
203
|
+
// Fallback to genesis if no tips available
|
|
198
204
|
if (tips.length === 0) {
|
|
199
205
|
return ['genesis'];
|
|
200
206
|
}
|
|
201
207
|
|
|
208
|
+
// Return all tips if we have fewer than requested
|
|
202
209
|
if (tips.length <= count) {
|
|
203
|
-
return tips;
|
|
210
|
+
return [...tips]; // Return copy to prevent mutation issues
|
|
204
211
|
}
|
|
205
212
|
|
|
206
213
|
// Weighted random selection based on cumulative weight
|
|
207
214
|
const selected = new Set();
|
|
208
215
|
const weights = tips.map(tipId => {
|
|
209
216
|
const tx = this.transactions.get(tipId);
|
|
210
|
-
return tx ? tx.cumulativeWeight : 1;
|
|
217
|
+
return tx ? Math.max(tx.cumulativeWeight, 1) : 1;
|
|
211
218
|
});
|
|
212
219
|
|
|
213
220
|
const totalWeight = weights.reduce((a, b) => a + b, 0);
|
|
214
221
|
|
|
215
|
-
|
|
222
|
+
// Safety: prevent infinite loop
|
|
223
|
+
let attempts = 0;
|
|
224
|
+
const maxAttempts = count * 10;
|
|
225
|
+
|
|
226
|
+
while (selected.size < count && selected.size < tips.length && attempts < maxAttempts) {
|
|
216
227
|
let random = Math.random() * totalWeight;
|
|
217
228
|
|
|
218
229
|
for (let i = 0; i < tips.length; i++) {
|
|
@@ -222,18 +233,26 @@ export class QDAG extends EventEmitter {
|
|
|
222
233
|
break;
|
|
223
234
|
}
|
|
224
235
|
}
|
|
236
|
+
attempts++;
|
|
225
237
|
}
|
|
226
238
|
|
|
227
|
-
|
|
239
|
+
// Ensure we have at least one valid parent
|
|
240
|
+
const result = Array.from(selected);
|
|
241
|
+
if (result.length === 0) {
|
|
242
|
+
result.push(tips[0] || 'genesis');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return result;
|
|
228
246
|
}
|
|
229
247
|
|
|
230
248
|
/**
|
|
231
249
|
* Add transaction to QDAG
|
|
232
250
|
*/
|
|
233
251
|
addTransaction(tx) {
|
|
234
|
-
// Validate transaction
|
|
235
|
-
|
|
236
|
-
|
|
252
|
+
// Validate transaction with detailed error
|
|
253
|
+
const validation = this.validateTransaction(tx, { returnError: true });
|
|
254
|
+
if (!validation.valid) {
|
|
255
|
+
throw new Error(`Invalid transaction: ${validation.error}`);
|
|
237
256
|
}
|
|
238
257
|
|
|
239
258
|
// Check for duplicates
|
|
@@ -304,35 +323,55 @@ export class QDAG extends EventEmitter {
|
|
|
304
323
|
|
|
305
324
|
/**
|
|
306
325
|
* Validate transaction
|
|
326
|
+
* @returns {boolean|{valid: boolean, error: string}}
|
|
307
327
|
*/
|
|
308
|
-
validateTransaction(tx) {
|
|
328
|
+
validateTransaction(tx, options = {}) {
|
|
329
|
+
const returnError = options.returnError || false;
|
|
330
|
+
const fail = (msg) => returnError ? { valid: false, error: msg } : false;
|
|
331
|
+
const pass = () => returnError ? { valid: true, error: null } : true;
|
|
332
|
+
|
|
309
333
|
// Check required fields
|
|
310
|
-
if (!tx.id
|
|
311
|
-
return
|
|
334
|
+
if (!tx.id) {
|
|
335
|
+
return fail('Missing transaction id');
|
|
336
|
+
}
|
|
337
|
+
if (!tx.timestamp) {
|
|
338
|
+
return fail('Missing transaction timestamp');
|
|
339
|
+
}
|
|
340
|
+
if (!tx.type) {
|
|
341
|
+
return fail('Missing transaction type');
|
|
312
342
|
}
|
|
313
343
|
|
|
314
|
-
//
|
|
315
|
-
if (tx.type
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
}
|
|
344
|
+
// Genesis transactions don't need parents
|
|
345
|
+
if (tx.type === 'genesis') {
|
|
346
|
+
return pass();
|
|
347
|
+
}
|
|
319
348
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
349
|
+
// Ensure genesis exists before validating non-genesis transactions
|
|
350
|
+
if (!this.transactions.has('genesis')) {
|
|
351
|
+
this.createGenesis();
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Check parents exist
|
|
355
|
+
if (!tx.parents || tx.parents.length === 0) {
|
|
356
|
+
return fail('Non-genesis transaction must have at least one parent');
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
for (const parentId of tx.parents) {
|
|
360
|
+
if (!this.transactions.has(parentId)) {
|
|
361
|
+
return fail(`Parent transaction not found: ${parentId}`);
|
|
324
362
|
}
|
|
325
363
|
}
|
|
326
364
|
|
|
327
|
-
// Check no cycles (parents must be older)
|
|
365
|
+
// Check no cycles (parents must be older or equal for simultaneous txs)
|
|
328
366
|
for (const parentId of tx.parents) {
|
|
329
367
|
const parent = this.transactions.get(parentId);
|
|
330
|
-
|
|
331
|
-
|
|
368
|
+
// Allow equal timestamps (transactions created at same time)
|
|
369
|
+
if (parent && parent.timestamp > tx.timestamp) {
|
|
370
|
+
return fail(`Parent ${parentId} has future timestamp`);
|
|
332
371
|
}
|
|
333
372
|
}
|
|
334
373
|
|
|
335
|
-
return
|
|
374
|
+
return pass();
|
|
336
375
|
}
|
|
337
376
|
|
|
338
377
|
/**
|
|
Binary file
|