@ruvector/edge-net 0.5.0 → 0.5.3
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/README.md +281 -10
- package/core-invariants.js +942 -0
- package/models/adapter-hub.js +1008 -0
- package/models/adapter-security.js +792 -0
- package/models/benchmark.js +688 -0
- package/models/distribution.js +791 -0
- package/models/index.js +109 -0
- package/models/integrity.js +753 -0
- package/models/loader.js +725 -0
- package/models/microlora.js +1298 -0
- package/models/model-loader.js +922 -0
- package/models/model-optimizer.js +1245 -0
- package/models/model-registry.js +696 -0
- package/models/model-utils.js +548 -0
- package/models/models-cli.js +914 -0
- package/models/registry.json +214 -0
- package/models/training-utils.js +1418 -0
- package/models/wasm-core.js +1025 -0
- package/network-genesis.js +2847 -0
- package/onnx-worker.js +462 -8
- package/package.json +33 -3
- package/plugins/SECURITY-AUDIT.md +654 -0
- package/plugins/cli.js +43 -3
- package/plugins/implementations/e2e-encryption.js +57 -12
- package/plugins/plugin-loader.js +610 -21
- package/tests/model-optimizer.test.js +644 -0
- package/tests/network-genesis.test.js +562 -0
- package/tests/plugin-benchmark.js +1239 -0
- package/tests/plugin-system-test.js +163 -0
- package/tests/wasm-core.test.js +368 -0
|
@@ -0,0 +1,1025 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ruvector/edge-net WASM Core
|
|
3
|
+
*
|
|
4
|
+
* Pure WASM implementation for cross-platform edge support:
|
|
5
|
+
* - Browsers (Chrome, Firefox, Safari, Edge)
|
|
6
|
+
* - Node.js 16+
|
|
7
|
+
* - Deno
|
|
8
|
+
* - Cloudflare Workers
|
|
9
|
+
* - Vercel Edge
|
|
10
|
+
* - Bun
|
|
11
|
+
*
|
|
12
|
+
* Uses ruvector_edge_net WASM module for:
|
|
13
|
+
* - Ed25519 signing/verification
|
|
14
|
+
* - SHA256/SHA512 hashing
|
|
15
|
+
* - Merkle tree operations
|
|
16
|
+
* - Canonical JSON encoding
|
|
17
|
+
*
|
|
18
|
+
* @module @ruvector/edge-net/models/wasm-core
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// PLATFORM DETECTION
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Detect current runtime environment
|
|
27
|
+
*/
|
|
28
|
+
export function detectPlatform() {
|
|
29
|
+
// Cloudflare Workers
|
|
30
|
+
if (typeof caches !== 'undefined' && typeof HTMLRewriter !== 'undefined') {
|
|
31
|
+
return 'cloudflare-workers';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Deno
|
|
35
|
+
if (typeof Deno !== 'undefined') {
|
|
36
|
+
return 'deno';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Bun
|
|
40
|
+
if (typeof Bun !== 'undefined') {
|
|
41
|
+
return 'bun';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Node.js
|
|
45
|
+
if (typeof process !== 'undefined' && process.versions?.node) {
|
|
46
|
+
return 'node';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Browser with WebAssembly
|
|
50
|
+
if (typeof window !== 'undefined' && typeof WebAssembly !== 'undefined') {
|
|
51
|
+
return 'browser';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Generic WebAssembly environment
|
|
55
|
+
if (typeof WebAssembly !== 'undefined') {
|
|
56
|
+
return 'wasm';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return 'unknown';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Platform capabilities
|
|
64
|
+
*/
|
|
65
|
+
export function getPlatformCapabilities() {
|
|
66
|
+
const platform = detectPlatform();
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
platform,
|
|
70
|
+
hasWebAssembly: typeof WebAssembly !== 'undefined',
|
|
71
|
+
hasSIMD: checkSIMDSupport(),
|
|
72
|
+
hasThreads: checkThreadsSupport(),
|
|
73
|
+
hasStreaming: typeof WebAssembly?.compileStreaming === 'function',
|
|
74
|
+
hasIndexedDB: typeof indexedDB !== 'undefined',
|
|
75
|
+
hasWebCrypto: typeof crypto?.subtle !== 'undefined',
|
|
76
|
+
hasP2P: typeof RTCPeerConnection !== 'undefined',
|
|
77
|
+
maxMemory: getMaxMemory(),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function checkSIMDSupport() {
|
|
82
|
+
try {
|
|
83
|
+
return WebAssembly.validate(new Uint8Array([
|
|
84
|
+
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
|
|
85
|
+
0x01, 0x05, 0x01, 0x60, 0x00, 0x01, 0x7b, 0x03,
|
|
86
|
+
0x02, 0x01, 0x00, 0x0a, 0x0a, 0x01, 0x08, 0x00,
|
|
87
|
+
0xfd, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0b
|
|
88
|
+
]));
|
|
89
|
+
} catch {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function checkThreadsSupport() {
|
|
95
|
+
try {
|
|
96
|
+
return typeof SharedArrayBuffer !== 'undefined' &&
|
|
97
|
+
typeof Atomics !== 'undefined';
|
|
98
|
+
} catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function getMaxMemory() {
|
|
104
|
+
// Browser
|
|
105
|
+
if (typeof navigator !== 'undefined' && navigator.deviceMemory) {
|
|
106
|
+
return navigator.deviceMemory * 1024 * 1024 * 1024;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Node.js
|
|
110
|
+
if (typeof process !== 'undefined') {
|
|
111
|
+
try {
|
|
112
|
+
const os = require('os');
|
|
113
|
+
return os.totalmem?.() || 4 * 1024 * 1024 * 1024;
|
|
114
|
+
} catch {
|
|
115
|
+
return 4 * 1024 * 1024 * 1024;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Default 4GB
|
|
120
|
+
return 4 * 1024 * 1024 * 1024;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ============================================================================
|
|
124
|
+
// WASM MODULE LOADING
|
|
125
|
+
// ============================================================================
|
|
126
|
+
|
|
127
|
+
let wasmModule = null;
|
|
128
|
+
let wasmInstance = null;
|
|
129
|
+
let wasmReady = false;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Initialize WASM module with platform-specific loading
|
|
133
|
+
*/
|
|
134
|
+
export async function initWasm(options = {}) {
|
|
135
|
+
if (wasmReady && wasmInstance) {
|
|
136
|
+
return wasmInstance;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const platform = detectPlatform();
|
|
140
|
+
const capabilities = getPlatformCapabilities();
|
|
141
|
+
|
|
142
|
+
console.log(`[WASM Core] Initializing on ${platform}`);
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
// Try to import the main WASM module
|
|
146
|
+
const wasmPath = options.wasmPath || findWasmPath();
|
|
147
|
+
|
|
148
|
+
if (capabilities.hasStreaming && platform !== 'cloudflare-workers') {
|
|
149
|
+
// Streaming compilation (faster)
|
|
150
|
+
const response = await fetch(wasmPath);
|
|
151
|
+
wasmModule = await WebAssembly.compileStreaming(response);
|
|
152
|
+
} else {
|
|
153
|
+
// Buffer-based compilation (Workers, fallback)
|
|
154
|
+
const wasmBytes = await loadWasmBytes(wasmPath);
|
|
155
|
+
wasmModule = await WebAssembly.compile(wasmBytes);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Instantiate with imports
|
|
159
|
+
wasmInstance = await WebAssembly.instantiate(wasmModule, getWasmImports());
|
|
160
|
+
wasmReady = true;
|
|
161
|
+
|
|
162
|
+
console.log(`[WASM Core] Ready with ${capabilities.hasSIMD ? 'SIMD' : 'no SIMD'}`);
|
|
163
|
+
|
|
164
|
+
return wasmInstance;
|
|
165
|
+
} catch (error) {
|
|
166
|
+
console.warn(`[WASM Core] Native WASM failed, using JS fallback:`, error.message);
|
|
167
|
+
|
|
168
|
+
// Use JavaScript fallback
|
|
169
|
+
wasmInstance = createJSFallback();
|
|
170
|
+
wasmReady = true;
|
|
171
|
+
|
|
172
|
+
return wasmInstance;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Find WASM file path based on environment
|
|
178
|
+
*/
|
|
179
|
+
function findWasmPath() {
|
|
180
|
+
const platform = detectPlatform();
|
|
181
|
+
|
|
182
|
+
switch (platform) {
|
|
183
|
+
case 'node':
|
|
184
|
+
case 'bun':
|
|
185
|
+
return new URL('../ruvector_edge_net_bg.wasm', import.meta.url).href;
|
|
186
|
+
case 'deno':
|
|
187
|
+
return new URL('../ruvector_edge_net_bg.wasm', import.meta.url).href;
|
|
188
|
+
case 'browser':
|
|
189
|
+
// Try multiple paths
|
|
190
|
+
return './ruvector_edge_net_bg.wasm';
|
|
191
|
+
case 'cloudflare-workers':
|
|
192
|
+
// Workers use bundled WASM
|
|
193
|
+
return '__WASM_MODULE__';
|
|
194
|
+
default:
|
|
195
|
+
return './ruvector_edge_net_bg.wasm';
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Load WASM bytes based on platform
|
|
201
|
+
*/
|
|
202
|
+
async function loadWasmBytes(path) {
|
|
203
|
+
const platform = detectPlatform();
|
|
204
|
+
|
|
205
|
+
switch (platform) {
|
|
206
|
+
case 'node': {
|
|
207
|
+
// Node 18+ has native fetch, use it for URLs
|
|
208
|
+
if (path.startsWith('http://') || path.startsWith('https://')) {
|
|
209
|
+
const response = await fetch(path);
|
|
210
|
+
return response.arrayBuffer();
|
|
211
|
+
}
|
|
212
|
+
// For file:// URLs or local paths
|
|
213
|
+
const fs = await import('fs/promises');
|
|
214
|
+
const { fileURLToPath } = await import('url');
|
|
215
|
+
// Convert file:// URL to path if needed
|
|
216
|
+
const filePath = path.startsWith('file://')
|
|
217
|
+
? fileURLToPath(path)
|
|
218
|
+
: path;
|
|
219
|
+
return fs.readFile(filePath);
|
|
220
|
+
}
|
|
221
|
+
case 'deno': {
|
|
222
|
+
return Deno.readFile(path);
|
|
223
|
+
}
|
|
224
|
+
case 'bun': {
|
|
225
|
+
return Bun.file(path).arrayBuffer();
|
|
226
|
+
}
|
|
227
|
+
default: {
|
|
228
|
+
const response = await fetch(path);
|
|
229
|
+
return response.arrayBuffer();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Shared WASM memory - created once before instantiation
|
|
236
|
+
*/
|
|
237
|
+
let sharedMemory = null;
|
|
238
|
+
|
|
239
|
+
function getSharedMemory() {
|
|
240
|
+
if (!sharedMemory) {
|
|
241
|
+
// Reasonable memory limits for edge platforms:
|
|
242
|
+
// initial: 256 pages (16MB), max: 1024 pages (64MB)
|
|
243
|
+
sharedMemory = new WebAssembly.Memory({ initial: 256, maximum: 1024 });
|
|
244
|
+
}
|
|
245
|
+
return sharedMemory;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* WASM imports for the module
|
|
250
|
+
* CRITICAL: Does NOT reference wasmInstance - uses shared memory instead
|
|
251
|
+
*/
|
|
252
|
+
function getWasmImports() {
|
|
253
|
+
const memory = getSharedMemory();
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
env: {
|
|
257
|
+
// Memory - use shared instance created BEFORE wasm instantiation
|
|
258
|
+
memory,
|
|
259
|
+
|
|
260
|
+
// Console - uses shared memory buffer (safe, memory exists)
|
|
261
|
+
console_log: (ptr, len) => {
|
|
262
|
+
const bytes = new Uint8Array(memory.buffer, ptr, len);
|
|
263
|
+
console.log(new TextDecoder().decode(bytes));
|
|
264
|
+
},
|
|
265
|
+
console_error: (ptr, len) => {
|
|
266
|
+
const bytes = new Uint8Array(memory.buffer, ptr, len);
|
|
267
|
+
console.error(new TextDecoder().decode(bytes));
|
|
268
|
+
},
|
|
269
|
+
|
|
270
|
+
// Time
|
|
271
|
+
now_ms: () => Date.now(),
|
|
272
|
+
|
|
273
|
+
// Random - uses shared memory buffer (safe)
|
|
274
|
+
get_random_bytes: (ptr, len) => {
|
|
275
|
+
const bytes = new Uint8Array(memory.buffer, ptr, len);
|
|
276
|
+
crypto.getRandomValues(bytes);
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
wasi_snapshot_preview1: {
|
|
280
|
+
// Minimal WASI stubs for compatibility
|
|
281
|
+
fd_write: () => 0,
|
|
282
|
+
fd_read: () => 0,
|
|
283
|
+
fd_close: () => 0,
|
|
284
|
+
environ_get: () => 0,
|
|
285
|
+
environ_sizes_get: () => 0,
|
|
286
|
+
proc_exit: () => {},
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// ============================================================================
|
|
292
|
+
// JAVASCRIPT FALLBACK
|
|
293
|
+
// ============================================================================
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Pure JavaScript fallback when WASM is unavailable
|
|
297
|
+
*/
|
|
298
|
+
function createJSFallback() {
|
|
299
|
+
return {
|
|
300
|
+
exports: {
|
|
301
|
+
// SHA256
|
|
302
|
+
sha256: async (data) => {
|
|
303
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
304
|
+
return new Uint8Array(hashBuffer);
|
|
305
|
+
},
|
|
306
|
+
|
|
307
|
+
// SHA512
|
|
308
|
+
sha512: async (data) => {
|
|
309
|
+
const hashBuffer = await crypto.subtle.digest('SHA-512', data);
|
|
310
|
+
return new Uint8Array(hashBuffer);
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
// Ed25519 (using SubtleCrypto if available)
|
|
314
|
+
// SECURITY: Fail closed - never return mock signatures
|
|
315
|
+
ed25519_sign: async (message, privateKey) => {
|
|
316
|
+
// SubtleCrypto Ed25519 support varies by platform
|
|
317
|
+
try {
|
|
318
|
+
const key = await crypto.subtle.importKey(
|
|
319
|
+
'raw',
|
|
320
|
+
privateKey,
|
|
321
|
+
{ name: 'Ed25519' },
|
|
322
|
+
false,
|
|
323
|
+
['sign']
|
|
324
|
+
);
|
|
325
|
+
const signature = await crypto.subtle.sign('Ed25519', key, message);
|
|
326
|
+
return new Uint8Array(signature);
|
|
327
|
+
} catch (error) {
|
|
328
|
+
// FAIL CLOSED: Do not return mock signatures - throw error
|
|
329
|
+
throw new Error(`[SECURITY] Ed25519 signing unavailable: ${error.message}. Install tweetnacl for platforms without native Ed25519.`);
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
|
|
333
|
+
ed25519_verify: async (message, signature, publicKey) => {
|
|
334
|
+
try {
|
|
335
|
+
const key = await crypto.subtle.importKey(
|
|
336
|
+
'raw',
|
|
337
|
+
publicKey,
|
|
338
|
+
{ name: 'Ed25519' },
|
|
339
|
+
false,
|
|
340
|
+
['verify']
|
|
341
|
+
);
|
|
342
|
+
return await crypto.subtle.verify('Ed25519', key, signature, message);
|
|
343
|
+
} catch (error) {
|
|
344
|
+
// FAIL CLOSED: If verification unavailable, reject
|
|
345
|
+
console.error('[SECURITY] Ed25519 verify unavailable:', error.message);
|
|
346
|
+
return false; // Reject signature when verification unavailable
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
|
|
350
|
+
// Merkle operations (pure JS)
|
|
351
|
+
merkle_root: (hashes) => {
|
|
352
|
+
return computeMerkleRootJS(hashes);
|
|
353
|
+
},
|
|
354
|
+
|
|
355
|
+
// Canonical JSON
|
|
356
|
+
canonical_json: (obj) => {
|
|
357
|
+
return canonicalizeJS(obj);
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Pure JS Merkle root computation
|
|
365
|
+
*/
|
|
366
|
+
function computeMerkleRootJS(hashes) {
|
|
367
|
+
if (hashes.length === 0) return new Uint8Array(32);
|
|
368
|
+
if (hashes.length === 1) return hashes[0];
|
|
369
|
+
|
|
370
|
+
let level = [...hashes];
|
|
371
|
+
|
|
372
|
+
while (level.length > 1) {
|
|
373
|
+
const nextLevel = [];
|
|
374
|
+
for (let i = 0; i < level.length; i += 2) {
|
|
375
|
+
const left = level[i];
|
|
376
|
+
const right = level[i + 1] || left;
|
|
377
|
+
// Concatenate and hash
|
|
378
|
+
const combined = new Uint8Array(left.length + right.length);
|
|
379
|
+
combined.set(left, 0);
|
|
380
|
+
combined.set(right, left.length);
|
|
381
|
+
// Use sync hash for fallback
|
|
382
|
+
nextLevel.push(hashSync(combined));
|
|
383
|
+
}
|
|
384
|
+
level = nextLevel;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return level[0];
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Synchronous hash for fallback (uses simple hash if crypto.subtle unavailable)
|
|
392
|
+
*/
|
|
393
|
+
function hashSync(data) {
|
|
394
|
+
// Simple FNV-1a hash for fallback (NOT cryptographically secure)
|
|
395
|
+
// In production, use a proper sync hash library
|
|
396
|
+
const FNV_PRIME = 0x01000193;
|
|
397
|
+
const FNV_OFFSET = 0x811c9dc5;
|
|
398
|
+
|
|
399
|
+
let hash = FNV_OFFSET;
|
|
400
|
+
for (let i = 0; i < data.length; i++) {
|
|
401
|
+
hash ^= data[i];
|
|
402
|
+
hash = Math.imul(hash, FNV_PRIME);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Expand to 32 bytes (not secure, just for structure)
|
|
406
|
+
const result = new Uint8Array(32);
|
|
407
|
+
for (let i = 0; i < 32; i++) {
|
|
408
|
+
result[i] = (hash >> (i % 4) * 8) & 0xff;
|
|
409
|
+
hash = Math.imul(hash, FNV_PRIME);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return result;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Canonical JSON for JS fallback
|
|
417
|
+
*/
|
|
418
|
+
function canonicalizeJS(obj) {
|
|
419
|
+
if (obj === null || obj === undefined) return 'null';
|
|
420
|
+
if (typeof obj === 'boolean') return obj ? 'true' : 'false';
|
|
421
|
+
if (typeof obj === 'number') {
|
|
422
|
+
if (!Number.isFinite(obj)) throw new Error('Cannot canonicalize Infinity/NaN');
|
|
423
|
+
return JSON.stringify(obj);
|
|
424
|
+
}
|
|
425
|
+
if (typeof obj === 'string') {
|
|
426
|
+
return JSON.stringify(obj).replace(/[\u007f-\uffff]/g, (c) =>
|
|
427
|
+
'\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4)
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
if (Array.isArray(obj)) {
|
|
431
|
+
return '[' + obj.map(canonicalizeJS).join(',') + ']';
|
|
432
|
+
}
|
|
433
|
+
if (typeof obj === 'object') {
|
|
434
|
+
const keys = Object.keys(obj).sort();
|
|
435
|
+
const pairs = keys
|
|
436
|
+
.filter(k => obj[k] !== undefined)
|
|
437
|
+
.map(k => canonicalizeJS(k) + ':' + canonicalizeJS(obj[k]));
|
|
438
|
+
return '{' + pairs.join(',') + '}';
|
|
439
|
+
}
|
|
440
|
+
throw new Error(`Cannot canonicalize: ${typeof obj}`);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// ============================================================================
|
|
444
|
+
// WASM CRYPTO API
|
|
445
|
+
// ============================================================================
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* WASM-accelerated cryptographic operations
|
|
449
|
+
*/
|
|
450
|
+
export class WasmCrypto {
|
|
451
|
+
constructor() {
|
|
452
|
+
this.ready = false;
|
|
453
|
+
this.instance = null;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
async init() {
|
|
457
|
+
this.instance = await initWasm();
|
|
458
|
+
this.ready = true;
|
|
459
|
+
return this;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* SHA256 hash
|
|
464
|
+
*/
|
|
465
|
+
async sha256(data) {
|
|
466
|
+
if (!this.ready) await this.init();
|
|
467
|
+
|
|
468
|
+
const input = typeof data === 'string'
|
|
469
|
+
? new TextEncoder().encode(data)
|
|
470
|
+
: new Uint8Array(data);
|
|
471
|
+
|
|
472
|
+
if (this.instance.exports.sha256) {
|
|
473
|
+
return this.instance.exports.sha256(input);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Fallback to SubtleCrypto
|
|
477
|
+
const hash = await crypto.subtle.digest('SHA-256', input);
|
|
478
|
+
return new Uint8Array(hash);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* SHA256 as hex string
|
|
483
|
+
*/
|
|
484
|
+
async sha256Hex(data) {
|
|
485
|
+
const hash = await this.sha256(data);
|
|
486
|
+
return Array.from(hash).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Ed25519 sign
|
|
491
|
+
*/
|
|
492
|
+
async sign(message, privateKey) {
|
|
493
|
+
if (!this.ready) await this.init();
|
|
494
|
+
|
|
495
|
+
const msgBytes = typeof message === 'string'
|
|
496
|
+
? new TextEncoder().encode(message)
|
|
497
|
+
: new Uint8Array(message);
|
|
498
|
+
|
|
499
|
+
return this.instance.exports.ed25519_sign(msgBytes, privateKey);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Ed25519 verify
|
|
504
|
+
*/
|
|
505
|
+
async verify(message, signature, publicKey) {
|
|
506
|
+
if (!this.ready) await this.init();
|
|
507
|
+
|
|
508
|
+
const msgBytes = typeof message === 'string'
|
|
509
|
+
? new TextEncoder().encode(message)
|
|
510
|
+
: new Uint8Array(message);
|
|
511
|
+
|
|
512
|
+
return this.instance.exports.ed25519_verify(msgBytes, signature, publicKey);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Compute Merkle root from chunk hashes
|
|
517
|
+
*/
|
|
518
|
+
async merkleRoot(chunkHashes) {
|
|
519
|
+
if (!this.ready) await this.init();
|
|
520
|
+
|
|
521
|
+
if (this.instance.exports.merkle_root) {
|
|
522
|
+
return this.instance.exports.merkle_root(chunkHashes);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// JS fallback
|
|
526
|
+
return computeMerkleRootJS(chunkHashes);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Canonical JSON encoding
|
|
531
|
+
*/
|
|
532
|
+
canonicalize(obj) {
|
|
533
|
+
return canonicalizeJS(obj);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Hash canonical JSON
|
|
538
|
+
*/
|
|
539
|
+
async hashCanonical(obj) {
|
|
540
|
+
const canonical = this.canonicalize(obj);
|
|
541
|
+
return this.sha256Hex(canonical);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// ============================================================================
|
|
546
|
+
// WASM MODEL INFERENCE
|
|
547
|
+
// ============================================================================
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* WASM-accelerated model inference
|
|
551
|
+
*/
|
|
552
|
+
export class WasmInference {
|
|
553
|
+
constructor(options = {}) {
|
|
554
|
+
this.ready = false;
|
|
555
|
+
this.model = null;
|
|
556
|
+
this.crypto = new WasmCrypto();
|
|
557
|
+
this.useSIMD = options.useSIMD ?? true;
|
|
558
|
+
this.useThreads = options.useThreads ?? false;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
async init() {
|
|
562
|
+
await this.crypto.init();
|
|
563
|
+
this.ready = true;
|
|
564
|
+
|
|
565
|
+
const caps = getPlatformCapabilities();
|
|
566
|
+
console.log(`[WASM Inference] Ready: SIMD=${caps.hasSIMD}, Threads=${caps.hasThreads}`);
|
|
567
|
+
|
|
568
|
+
return this;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Load ONNX model into WASM runtime
|
|
573
|
+
*/
|
|
574
|
+
async loadModel(modelData, manifest) {
|
|
575
|
+
if (!this.ready) await this.init();
|
|
576
|
+
|
|
577
|
+
// Verify model integrity first
|
|
578
|
+
const hash = await this.crypto.sha256Hex(modelData);
|
|
579
|
+
// Support both manifest formats: artifacts.model.sha256 and artifacts[0].sha256
|
|
580
|
+
const expected = manifest.artifacts?.model?.sha256 ||
|
|
581
|
+
manifest.artifacts?.[0]?.sha256 ||
|
|
582
|
+
manifest.model?.sha256;
|
|
583
|
+
|
|
584
|
+
if (expected && hash !== expected) {
|
|
585
|
+
throw new Error(`Model hash mismatch: ${hash} !== ${expected}`);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// In production, this would initialize ONNX runtime in WASM
|
|
589
|
+
// For now, we store the model data
|
|
590
|
+
this.model = {
|
|
591
|
+
data: modelData,
|
|
592
|
+
manifest,
|
|
593
|
+
loadedAt: Date.now(),
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
return this;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Run inference (placeholder for ONNX WASM runtime)
|
|
601
|
+
*/
|
|
602
|
+
async infer(input, options = {}) {
|
|
603
|
+
if (!this.model) {
|
|
604
|
+
throw new Error('No model loaded');
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// In production, this would call ONNX WASM runtime
|
|
608
|
+
// For now, return placeholder
|
|
609
|
+
console.log('[WASM Inference] Would run inference on:', typeof input);
|
|
610
|
+
|
|
611
|
+
return {
|
|
612
|
+
output: null,
|
|
613
|
+
timeMs: 0,
|
|
614
|
+
platform: detectPlatform(),
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Generate embeddings
|
|
620
|
+
*/
|
|
621
|
+
async embed(texts) {
|
|
622
|
+
if (!this.model) {
|
|
623
|
+
throw new Error('No model loaded');
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
const inputTexts = Array.isArray(texts) ? texts : [texts];
|
|
627
|
+
|
|
628
|
+
// Placeholder - in production, run through ONNX
|
|
629
|
+
const embeddings = inputTexts.map(text => {
|
|
630
|
+
// Generate deterministic pseudo-embedding from text hash
|
|
631
|
+
const hash = this.crypto.canonicalize(text);
|
|
632
|
+
const embedding = new Float32Array(384);
|
|
633
|
+
for (let i = 0; i < 384; i++) {
|
|
634
|
+
embedding[i] = (hash.charCodeAt(i % hash.length) - 64) / 64;
|
|
635
|
+
}
|
|
636
|
+
return embedding;
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
return {
|
|
640
|
+
embeddings,
|
|
641
|
+
model: this.model.manifest?.model?.id || 'unknown',
|
|
642
|
+
platform: detectPlatform(),
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Unload model and free memory
|
|
648
|
+
*/
|
|
649
|
+
unload() {
|
|
650
|
+
this.model = null;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// ============================================================================
|
|
655
|
+
// GENESIS BIRTHING SYSTEM (WASM)
|
|
656
|
+
// ============================================================================
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* WASM-native network genesis/birthing system
|
|
660
|
+
*
|
|
661
|
+
* Creates new network instances with:
|
|
662
|
+
* - Cryptographic identity (Ed25519 keypair)
|
|
663
|
+
* - Lineage tracking (Merkle DAG)
|
|
664
|
+
* - Cross-platform compatibility
|
|
665
|
+
* - Cryptographic signing of genesis blocks
|
|
666
|
+
*/
|
|
667
|
+
export class WasmGenesis {
|
|
668
|
+
constructor(options = {}) {
|
|
669
|
+
this.crypto = new WasmCrypto();
|
|
670
|
+
this.ready = false;
|
|
671
|
+
|
|
672
|
+
// Genesis configuration
|
|
673
|
+
this.config = {
|
|
674
|
+
networkName: options.networkName || 'edge-net',
|
|
675
|
+
version: options.version || '1.0.0',
|
|
676
|
+
parentId: options.parentId || null,
|
|
677
|
+
traits: options.traits || {},
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
// Network keypair (generated during birth)
|
|
681
|
+
this.keypair = null;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
async init() {
|
|
685
|
+
await this.crypto.init();
|
|
686
|
+
this.ready = true;
|
|
687
|
+
return this;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Generate Ed25519 keypair for network identity
|
|
692
|
+
* SECURITY: Uses WebCrypto for key generation
|
|
693
|
+
*/
|
|
694
|
+
async _generateKeypair() {
|
|
695
|
+
try {
|
|
696
|
+
const keyPair = await crypto.subtle.generateKey(
|
|
697
|
+
{ name: 'Ed25519' },
|
|
698
|
+
true, // extractable for export
|
|
699
|
+
['sign', 'verify']
|
|
700
|
+
);
|
|
701
|
+
|
|
702
|
+
// Export public key
|
|
703
|
+
const publicKeyRaw = await crypto.subtle.exportKey('raw', keyPair.publicKey);
|
|
704
|
+
|
|
705
|
+
return {
|
|
706
|
+
privateKey: keyPair.privateKey,
|
|
707
|
+
publicKey: keyPair.publicKey,
|
|
708
|
+
publicKeyBytes: new Uint8Array(publicKeyRaw),
|
|
709
|
+
publicKeyHex: Array.from(new Uint8Array(publicKeyRaw))
|
|
710
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
711
|
+
.join(''),
|
|
712
|
+
};
|
|
713
|
+
} catch (error) {
|
|
714
|
+
throw new Error(`[SECURITY] Cannot generate Ed25519 keypair: ${error.message}. Native Ed25519 required for genesis.`);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Sign data with network private key
|
|
720
|
+
*/
|
|
721
|
+
async _signWithKeypair(data, keypair) {
|
|
722
|
+
const dataBytes = typeof data === 'string'
|
|
723
|
+
? new TextEncoder().encode(data)
|
|
724
|
+
: new Uint8Array(data);
|
|
725
|
+
|
|
726
|
+
try {
|
|
727
|
+
const signature = await crypto.subtle.sign(
|
|
728
|
+
{ name: 'Ed25519' },
|
|
729
|
+
keypair.privateKey,
|
|
730
|
+
dataBytes
|
|
731
|
+
);
|
|
732
|
+
|
|
733
|
+
return {
|
|
734
|
+
signature: new Uint8Array(signature),
|
|
735
|
+
signatureHex: Array.from(new Uint8Array(signature))
|
|
736
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
737
|
+
.join(''),
|
|
738
|
+
};
|
|
739
|
+
} catch (error) {
|
|
740
|
+
throw new Error(`[SECURITY] Signing failed: ${error.message}`);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Birth a new network instance with cryptographic signing
|
|
746
|
+
*/
|
|
747
|
+
async birthNetwork(options = {}) {
|
|
748
|
+
if (!this.ready) await this.init();
|
|
749
|
+
|
|
750
|
+
const timestamp = Date.now();
|
|
751
|
+
|
|
752
|
+
// Generate network keypair
|
|
753
|
+
this.keypair = await this._generateKeypair();
|
|
754
|
+
|
|
755
|
+
// Generate network identity
|
|
756
|
+
const networkId = await this._generateNetworkId(timestamp);
|
|
757
|
+
|
|
758
|
+
// Create genesis block (unsigned payload)
|
|
759
|
+
const genesisBlock = {
|
|
760
|
+
networkId,
|
|
761
|
+
version: this.config.version,
|
|
762
|
+
birthTimestamp: timestamp,
|
|
763
|
+
parentId: this.config.parentId,
|
|
764
|
+
traits: {
|
|
765
|
+
...this.config.traits,
|
|
766
|
+
...options.traits,
|
|
767
|
+
},
|
|
768
|
+
capabilities: options.capabilities || ['embed', 'generate'],
|
|
769
|
+
platform: detectPlatform(),
|
|
770
|
+
platformCapabilities: getPlatformCapabilities(),
|
|
771
|
+
// Include public key for verification
|
|
772
|
+
publicKey: this.keypair.publicKeyHex,
|
|
773
|
+
keyAlgorithm: 'Ed25519',
|
|
774
|
+
};
|
|
775
|
+
|
|
776
|
+
// Compute canonical hash of genesis block
|
|
777
|
+
const genesisCanonical = this.crypto.canonicalize(genesisBlock);
|
|
778
|
+
const genesisHash = await this.crypto.sha256Hex(genesisCanonical);
|
|
779
|
+
|
|
780
|
+
// SECURITY: Sign the genesis hash with network keypair
|
|
781
|
+
const { signature, signatureHex } = await this._signWithKeypair(genesisHash, this.keypair);
|
|
782
|
+
|
|
783
|
+
// Create network manifest with signature
|
|
784
|
+
const manifest = {
|
|
785
|
+
schemaVersion: '2.0.0',
|
|
786
|
+
genesis: genesisBlock,
|
|
787
|
+
integrity: {
|
|
788
|
+
genesisHash,
|
|
789
|
+
signatureAlgorithm: 'Ed25519',
|
|
790
|
+
signature: signatureHex,
|
|
791
|
+
signedPayload: 'genesis',
|
|
792
|
+
merkleRoot: await this.crypto.merkleRoot([
|
|
793
|
+
await this.crypto.sha256(genesisCanonical),
|
|
794
|
+
]),
|
|
795
|
+
},
|
|
796
|
+
lineage: this.config.parentId ? {
|
|
797
|
+
parentId: this.config.parentId,
|
|
798
|
+
generation: options.generation || 1,
|
|
799
|
+
inheritedTraits: options.inheritedTraits || [],
|
|
800
|
+
} : null,
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
return {
|
|
804
|
+
networkId,
|
|
805
|
+
manifest,
|
|
806
|
+
genesisHash,
|
|
807
|
+
signature: signatureHex,
|
|
808
|
+
publicKey: this.keypair.publicKeyHex,
|
|
809
|
+
platform: detectPlatform(),
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Generate unique network ID using cryptographic randomness
|
|
815
|
+
* SECURITY: Uses crypto.getRandomValues instead of Math.random()
|
|
816
|
+
*/
|
|
817
|
+
async _generateNetworkId(timestamp) {
|
|
818
|
+
// Generate 16 bytes of cryptographic randomness
|
|
819
|
+
const randomBytes = new Uint8Array(16);
|
|
820
|
+
crypto.getRandomValues(randomBytes);
|
|
821
|
+
const randomHex = Array.from(randomBytes)
|
|
822
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
823
|
+
.join('');
|
|
824
|
+
|
|
825
|
+
const seed = `${this.config.networkName}:${timestamp}:${randomHex}`;
|
|
826
|
+
const hash = await this.crypto.sha256Hex(seed);
|
|
827
|
+
return `net_${hash.slice(0, 16)}`;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/**
|
|
831
|
+
* Verify a network's genesis signature
|
|
832
|
+
* SECURITY: Validates cryptographic signature of genesis block
|
|
833
|
+
*/
|
|
834
|
+
async verifyGenesis(manifest) {
|
|
835
|
+
const genesis = manifest.genesis;
|
|
836
|
+
const integrity = manifest.integrity;
|
|
837
|
+
|
|
838
|
+
if (!genesis.publicKey || !integrity.signature) {
|
|
839
|
+
return { valid: false, error: 'Missing public key or signature' };
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
try {
|
|
843
|
+
// Reconstruct the canonical hash
|
|
844
|
+
const genesisCanonical = this.crypto.canonicalize(genesis);
|
|
845
|
+
const genesisHash = await this.crypto.sha256Hex(genesisCanonical);
|
|
846
|
+
|
|
847
|
+
// Verify hash matches
|
|
848
|
+
if (genesisHash !== integrity.genesisHash) {
|
|
849
|
+
return { valid: false, error: 'Genesis hash mismatch' };
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// Convert hex strings back to bytes
|
|
853
|
+
const publicKeyBytes = new Uint8Array(
|
|
854
|
+
genesis.publicKey.match(/.{2}/g).map(b => parseInt(b, 16))
|
|
855
|
+
);
|
|
856
|
+
const signatureBytes = new Uint8Array(
|
|
857
|
+
integrity.signature.match(/.{2}/g).map(b => parseInt(b, 16))
|
|
858
|
+
);
|
|
859
|
+
const hashBytes = new TextEncoder().encode(genesisHash);
|
|
860
|
+
|
|
861
|
+
// Import public key and verify
|
|
862
|
+
const publicKey = await crypto.subtle.importKey(
|
|
863
|
+
'raw',
|
|
864
|
+
publicKeyBytes,
|
|
865
|
+
{ name: 'Ed25519' },
|
|
866
|
+
false,
|
|
867
|
+
['verify']
|
|
868
|
+
);
|
|
869
|
+
|
|
870
|
+
const valid = await crypto.subtle.verify(
|
|
871
|
+
{ name: 'Ed25519' },
|
|
872
|
+
publicKey,
|
|
873
|
+
signatureBytes,
|
|
874
|
+
hashBytes
|
|
875
|
+
);
|
|
876
|
+
|
|
877
|
+
return { valid, genesisHash };
|
|
878
|
+
} catch (error) {
|
|
879
|
+
return { valid: false, error: `Verification failed: ${error.message}` };
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* Verify a network's lineage with cryptographic validation
|
|
885
|
+
*/
|
|
886
|
+
async verifyLineage(manifest, parentManifest = null) {
|
|
887
|
+
// First verify genesis signature
|
|
888
|
+
const genesisResult = await this.verifyGenesis(manifest);
|
|
889
|
+
if (!genesisResult.valid) {
|
|
890
|
+
return { valid: false, error: `Genesis invalid: ${genesisResult.error}` };
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
if (!manifest.lineage) {
|
|
894
|
+
return { valid: true, isRoot: true, genesisVerified: true };
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
if (!parentManifest) {
|
|
898
|
+
return { valid: false, error: 'Parent manifest required for lineage verification' };
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
// Verify parent genesis signature
|
|
902
|
+
const parentGenesisResult = await this.verifyGenesis(parentManifest);
|
|
903
|
+
if (!parentGenesisResult.valid) {
|
|
904
|
+
return { valid: false, error: `Parent genesis invalid: ${parentGenesisResult.error}` };
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// Verify parent ID matches
|
|
908
|
+
if (manifest.lineage.parentId !== parentManifest.genesis.networkId) {
|
|
909
|
+
return { valid: false, error: 'Parent ID mismatch' };
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// Verify generation is sequential
|
|
913
|
+
const parentGen = parentManifest.lineage?.generation || 0;
|
|
914
|
+
if (manifest.lineage.generation !== parentGen + 1) {
|
|
915
|
+
return { valid: false, error: 'Generation sequence broken' };
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
return {
|
|
919
|
+
valid: true,
|
|
920
|
+
parentId: manifest.lineage.parentId,
|
|
921
|
+
generation: manifest.lineage.generation,
|
|
922
|
+
genesisVerified: true,
|
|
923
|
+
parentVerified: true,
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
/**
|
|
928
|
+
* Create a child network (reproduction)
|
|
929
|
+
*/
|
|
930
|
+
async reproduce(parentManifest, options = {}) {
|
|
931
|
+
if (!this.ready) await this.init();
|
|
932
|
+
|
|
933
|
+
// Mutate traits from parent
|
|
934
|
+
const mutatedTraits = this._mutateTraits(
|
|
935
|
+
parentManifest.genesis.traits,
|
|
936
|
+
options.mutationRate || 0.1
|
|
937
|
+
);
|
|
938
|
+
|
|
939
|
+
// Birth child network
|
|
940
|
+
const child = await this.birthNetwork({
|
|
941
|
+
traits: mutatedTraits,
|
|
942
|
+
capabilities: options.capabilities || parentManifest.genesis.capabilities,
|
|
943
|
+
generation: (parentManifest.lineage?.generation || 0) + 1,
|
|
944
|
+
inheritedTraits: Object.keys(parentManifest.genesis.traits),
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
// Update config for lineage
|
|
948
|
+
child.manifest.lineage = {
|
|
949
|
+
parentId: parentManifest.genesis.networkId,
|
|
950
|
+
generation: (parentManifest.lineage?.generation || 0) + 1,
|
|
951
|
+
inheritedTraits: Object.keys(parentManifest.genesis.traits),
|
|
952
|
+
mutatedTraits: Object.keys(mutatedTraits).filter(
|
|
953
|
+
k => mutatedTraits[k] !== parentManifest.genesis.traits[k]
|
|
954
|
+
),
|
|
955
|
+
};
|
|
956
|
+
|
|
957
|
+
return child;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
/**
|
|
961
|
+
* Mutate traits for evolution using cryptographic randomness
|
|
962
|
+
* SECURITY: Uses crypto.getRandomValues for unpredictable mutations
|
|
963
|
+
*/
|
|
964
|
+
_mutateTraits(parentTraits, mutationRate) {
|
|
965
|
+
const mutated = { ...parentTraits };
|
|
966
|
+
|
|
967
|
+
// Generate random bytes for mutation decisions
|
|
968
|
+
const randomBytes = new Uint8Array(Object.keys(mutated).length * 2);
|
|
969
|
+
crypto.getRandomValues(randomBytes);
|
|
970
|
+
|
|
971
|
+
let idx = 0;
|
|
972
|
+
for (const [key, value] of Object.entries(mutated)) {
|
|
973
|
+
// Use crypto random for mutation probability check
|
|
974
|
+
const shouldMutate = (randomBytes[idx++] / 255) < mutationRate;
|
|
975
|
+
|
|
976
|
+
if (typeof value === 'number' && shouldMutate) {
|
|
977
|
+
// Use crypto random for mutation amount (+/- 10%)
|
|
978
|
+
const mutationFactor = ((randomBytes[idx++] / 255) - 0.5) * 0.2;
|
|
979
|
+
mutated[key] = value * (1 + mutationFactor);
|
|
980
|
+
} else {
|
|
981
|
+
idx++; // Skip unused random byte
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
return mutated;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
// ============================================================================
|
|
990
|
+
// SINGLETON INSTANCES
|
|
991
|
+
// ============================================================================
|
|
992
|
+
|
|
993
|
+
let cryptoInstance = null;
|
|
994
|
+
let genesisInstance = null;
|
|
995
|
+
|
|
996
|
+
export async function getCrypto() {
|
|
997
|
+
if (!cryptoInstance) {
|
|
998
|
+
cryptoInstance = new WasmCrypto();
|
|
999
|
+
await cryptoInstance.init();
|
|
1000
|
+
}
|
|
1001
|
+
return cryptoInstance;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
export async function getGenesis(options = {}) {
|
|
1005
|
+
if (!genesisInstance) {
|
|
1006
|
+
genesisInstance = new WasmGenesis(options);
|
|
1007
|
+
await genesisInstance.init();
|
|
1008
|
+
}
|
|
1009
|
+
return genesisInstance;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
// ============================================================================
|
|
1013
|
+
// EXPORTS
|
|
1014
|
+
// ============================================================================
|
|
1015
|
+
|
|
1016
|
+
export default {
|
|
1017
|
+
detectPlatform,
|
|
1018
|
+
getPlatformCapabilities,
|
|
1019
|
+
initWasm,
|
|
1020
|
+
WasmCrypto,
|
|
1021
|
+
WasmInference,
|
|
1022
|
+
WasmGenesis,
|
|
1023
|
+
getCrypto,
|
|
1024
|
+
getGenesis,
|
|
1025
|
+
};
|