@anonympins/fingerprint 0.7.0 → 0.7.2

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.
Files changed (66) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +1 -1
  3. package/composer.json +8 -1
  4. package/package.json +1 -1
  5. package/public/fingerprint-wordpress.zip +0 -0
  6. package/src/js/build-client.js +8 -10
  7. package/src/js/fingerprint.builder.js +37 -38
  8. package/src/js/fingerprint.client.js +1531 -1463
  9. package/src/js/fingerprint.client.obfuscated.js +1 -0
  10. package/src/js/fingerprint.js +190 -16
  11. package/src/js/fingerprint.utils.js +213 -213
  12. package/src/js/library.js +340 -376
  13. package/src/js/mongodb-store.js +1 -1
  14. package/src/js/optimization.worker.js +27 -27
  15. package/src/js/pow.solver.inline.js +154 -56
  16. package/src/js/pow.solver.js +180 -92
  17. package/src/js/pow.worker.js +48 -48
  18. package/src/js/redis-store.js +1 -1
  19. package/src/js/tests/fingerprint.client.init.test.js +146 -143
  20. package/src/js/tests/fingerprint.test.js +124 -10
  21. package/src/js/tests/ip-reputation.test.js +1 -0
  22. package/src/js/upow-model-task.js +14 -15
  23. package/src/php/AutoTuner.php +34 -34
  24. package/src/php/Challenge/ChallengeUtils.php +57 -11
  25. package/src/php/Config/SecurityProfiles.php +20 -0
  26. package/src/php/DirectFingerprint.php +190 -145
  27. package/src/php/FingerprintBuilder.php +29 -30
  28. package/src/php/FingerprintClient.php +147 -148
  29. package/src/php/FingerprintEngine.php +44 -17
  30. package/src/php/Ja3AnomalyDetector.php +33 -32
  31. package/src/php/Optimization/FunctionRegistry.php +7 -7
  32. package/src/php/Optimization/Optimization.php +24 -24
  33. package/src/php/Optimization/OptimizationOperators.php +26 -25
  34. package/src/php/Optimization/ProblemInitializers.php +52 -52
  35. package/src/php/ProblemManager.php +31 -34
  36. package/src/php/RequestContext.php +8 -1
  37. package/src/php/Store/IStore.php +7 -8
  38. package/src/php/Store/InMemoryStore.php +66 -66
  39. package/src/php/Store/MongoDbStore.php +5 -5
  40. package/src/php/Store/RedisStore.php +5 -5
  41. package/src/php/Store/StoreManager.php +35 -35
  42. package/src/php/Tests/ChallengeUtilsTest.php +453 -453
  43. package/src/php/Tests/DnsCircuitBreakerTest.php +105 -105
  44. package/src/php/Tests/FingerprintEngineTest.php +38 -0
  45. package/src/php/Tests/IpReputationTest.php +10 -0
  46. package/src/php/Tests/MaliciousPatternsTest.php +103 -103
  47. package/src/php/Tests/MetricsTest.php +97 -97
  48. package/src/php/Tests/ProblemManagerTest.php +376 -376
  49. package/src/php/Tests/QuicFingerprintTest.php +53 -53
  50. package/src/php/Tests/RequestUtilsTest.php +471 -471
  51. package/src/php/Tests/TLSClientHelloParserTest.php +177 -177
  52. package/src/php/Tests/config/ed25519_key.json +3 -3
  53. package/src/php/Utils/BlockList.php +99 -99
  54. package/src/php/Utils/Env.php +49 -49
  55. package/src/php/Utils/MaliciousPatterns.php +74 -74
  56. package/src/php/Utils/MetricsManager.php +209 -209
  57. package/src/php/Utils/RequestUtils.php +129 -7
  58. package/src/php/Utils/TLSClientHelloParser.php +369 -369
  59. package/src/php/WordPress/WpDbStore.php +155 -0
  60. package/src/php/WordPress/fingerprint-wordpress.php +554 -0
  61. package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.mo +0 -0
  62. package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.po +262 -0
  63. package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.mo +0 -0
  64. package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.po +268 -0
  65. package/src/php/WordPress/package.php +218 -0
  66. package/src/php/bin/auto-tune.php +118 -118
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * @file @/pow.solver.js
3
- * @description Contient les fonctions côté client pour résoudre les différents types de challenges Proof-of-Work.
4
- * IMPORTANT: Pour les tâches d'optimisation, ce fichier a besoin d'accéder aux algorithmes de `library.js`.
5
- * Dans un vrai projet, il faudrait bundler une version client de `library.js` et l'importer ici.
6
- * Pour cet exemple, nous allons copier/coller les fonctions nécessaires.
7
- * Fichier compatible à la fois avec l'import de modules ES6 et l'injection directe dans un script HTML.
3
+ * @description Client-side functions to solve various Proof-of-Work challenges.
4
+ * IMPORTANT: For optimization tasks, this file requires access to algorithms in `library.js`.
5
+ * In production, a client bundle of `library.js` should be bundled and imported here.
6
+ * For this implementation, the necessary functions are embedded.
7
+ * Compatible with both ES6 module imports and direct HTML script injection.
8
8
  */
9
9
 
10
10
  'use strict';
@@ -17,6 +17,88 @@ function secureRandom() {
17
17
  }
18
18
  return Math.random();
19
19
  }
20
+ /**
21
+ * Standalone synchronous SHA-256 implementation usable anywhere (HTTP, insecure contexts, Workers).
22
+ */
23
+ function sha256Sync(bytes) {
24
+ if (bytes instanceof ArrayBuffer) {
25
+ bytes = new Uint8Array(bytes);
26
+ } else if (ArrayBuffer.isView(bytes) && !(bytes instanceof Uint8Array)) {
27
+ bytes = new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength);
28
+ }
29
+ var K = [
30
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
31
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
32
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76ca1422,
33
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
34
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
35
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
36
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
37
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
38
+ ];
39
+ var H = [
40
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
41
+ 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
42
+ ];
43
+ var l = bytes.length;
44
+ var bitLen = l * 8;
45
+ var padLen = ((l + 8) >> 6 << 6) + 64;
46
+ var buf = new Uint8Array(padLen);
47
+ buf.set(bytes);
48
+ buf[l] = 0x80;
49
+ var view = new DataView(buf.buffer);
50
+ view.setUint32(padLen - 4, bitLen >>> 0);
51
+ view.setUint32(padLen - 8, Math.floor(bitLen / 0x100000000));
52
+
53
+ var W = new Int32Array(64);
54
+ for (var i = 0; i < padLen; i += 64) {
55
+ for (var t = 0; t < 16; t++) {
56
+ W[t] = view.getInt32(i + t * 4);
57
+ }
58
+ for (var t = 16; t < 64; t++) {
59
+ var s0 = ((W[t - 15] >>> 7) | (W[t - 15] << 25)) ^ ((W[t - 15] >>> 18) | (W[t - 15] << 14)) ^ (W[t - 15] >>> 3);
60
+ var s1 = ((W[t - 2] >>> 17) | (W[t - 2] << 15)) ^ ((W[t - 2] >>> 19) | (W[t - 2] << 13)) ^ (W[t - 2] >>> 10);
61
+ W[t] = (W[t - 16] + s0 + W[t - 7] + s1) | 0;
62
+ }
63
+ var a = H[0], b = H[1], c = H[2], d = H[3], e = H[4], f = H[5], g = H[6], h = H[7];
64
+ for (var t = 0; t < 64; t++) {
65
+ var S1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7));
66
+ var ch = (e & f) ^ ((~e) & g);
67
+ var temp1 = (h + S1 + ch + K[t] + W[t]) | 0;
68
+ var S0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10));
69
+ var maj = (a & b) ^ (a & c) ^ (b & c);
70
+ var temp2 = (S0 + maj) | 0;
71
+ h = g; g = f; f = e; e = (d + temp1) | 0;
72
+ d = c; c = b; b = a; a = (temp1 + temp2) | 0;
73
+ }
74
+ H[0] = (H[0] + a) | 0;
75
+ H[1] = (H[1] + b) | 0;
76
+ H[2] = (H[2] + c) | 0;
77
+ H[3] = (H[3] + d) | 0;
78
+ H[4] = (H[4] + e) | 0;
79
+ H[5] = (H[5] + f) | 0;
80
+ H[6] = (H[6] + g) | 0;
81
+ H[7] = (H[7] + h) | 0;
82
+ }
83
+ var hex = '';
84
+ for (var i = 0; i < 8; i++) {
85
+ hex += (H[i] >>> 0).toString(16).padStart(8, '0');
86
+ }
87
+ return hex;
88
+ }
89
+
90
+ async function safeDigestSha256Hex(bytes) {
91
+ const cryptoObj = (typeof globalThis !== 'undefined' && globalThis.crypto) ? globalThis.crypto : (typeof window !== 'undefined' ? window.crypto : null);
92
+ if (cryptoObj && cryptoObj.subtle && typeof cryptoObj.subtle.digest === 'function') {
93
+ try {
94
+ const buf = await cryptoObj.subtle.digest("SHA-256", bytes);
95
+ return Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2, '0')).join('');
96
+ } catch (e) {
97
+ // Transparent fallback
98
+ }
99
+ }
100
+ return sha256Sync(bytes);
101
+ }
20
102
 
21
103
  function cyrb53(str, seed = 0) {
22
104
  const safeStr = (typeof str === 'string' ? str : String(str || '')).slice(0, 10000);
@@ -158,11 +240,11 @@ export async function solveSpaceChallenge(seed, queries, nonce, clientSecret, pe
158
240
  }
159
241
 
160
242
  /**
161
- * Résout un challenge CPU basé sur une cible en utilisant un bloc de base binaire.
162
- * @param {Uint8Array} baseBlock - Le bloc de données initial (nonce, secret, fp) fourni par le serveur.
163
- * @param {bigint} target - La cible à atteindre.
164
- * @param {Function} progressCallback - Callback pour les mises à jour de progression.
165
- * @returns {Promise<number>} La solution (un nombre entier).
243
+ * Solves a target-based CPU challenge using a binary base block.
244
+ * @param {Uint8Array} baseBlock - Initial data block (nonce, secret, fp) provided by the server.
245
+ * @param {bigint} target - Target difficulty to reach.
246
+ * @param {Function} progressCallback - Progress update callback.
247
+ * @returns {Promise<number>} Solution integer.
166
248
  */
167
249
  export async function solveCpuTargetInline(baseBlock, target, progressCallback) {
168
250
  // --- FIX: Add validation for the target to prevent BigInt conversion errors ---
@@ -198,6 +280,7 @@ export async function solveCpuTargetInline(baseBlock, target, progressCallback)
198
280
  if (typeof window !== 'undefined' && typeof Worker !== 'undefined') {
199
281
  try {
200
282
  const staticWorkerPath = window.ClientLibrary?.workerPath || (window.ClientConfig && window.ClientConfig.workerPath);
283
+ const sha256FnStr = sha256Sync.toString();
201
284
  if (staticWorkerPath) {
202
285
  return await new Promise((resolve, reject) => {
203
286
  const worker = new Worker(staticWorkerPath);
@@ -208,7 +291,7 @@ export async function solveCpuTargetInline(baseBlock, target, progressCallback)
208
291
  resolve(event.data.solution);
209
292
  worker.terminate();
210
293
  } else if (event.data.solution !== undefined) {
211
- // Fallback pour compatibilité avec l'ancienne signature
294
+ // Fallback for backward compatibility with previous signature
212
295
  resolve(event.data.solution);
213
296
  worker.terminate();
214
297
  }
@@ -226,15 +309,29 @@ export async function solveCpuTargetInline(baseBlock, target, progressCallback)
226
309
  const { baseBlock, target } = e.data;
227
310
  const cpuTarget = BigInt(target);
228
311
  const encoder = new TextEncoder();
312
+ const hasSubtle = typeof self.crypto !== 'undefined' && self.crypto && self.crypto.subtle && typeof self.crypto.subtle.digest === 'function';
313
+ const sha256Fallback = ${sha256FnStr};
229
314
  let cpuSolution = 0;
230
315
  while (true) {
231
316
  const solutionBytes = encoder.encode(String(cpuSolution));
232
317
  const finalBlock = new Uint8Array(baseBlock.length + solutionBytes.length);
233
318
  finalBlock.set(baseBlock);
234
319
  finalBlock.set(solutionBytes, baseBlock.length);
235
- const buf = await crypto.subtle.digest("SHA-256", finalBlock);
236
- const hashHex = Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2, '0')).join('');
237
- if (BigInt('0x' + hashHex) < cpuTarget) break;
320
+ let hashHex = '';
321
+ if (hasSubtle) {
322
+ try {
323
+ const buf = await self.crypto.subtle.digest("SHA-256", finalBlock);
324
+ const arr = new Uint8Array(buf);
325
+ for (let j = 0; j < arr.length; j++) {
326
+ hashHex += arr[j].toString(16).padStart(2, '0');
327
+ }
328
+ } catch (subtleErr) {
329
+ hashHex = sha256Fallback(finalBlock);
330
+ }
331
+ } else {
332
+ hashHex = sha256Fallback(finalBlock);
333
+ }
334
+ if (BigInt('0x' + hashHex) < cpuTarget) break;
238
335
  cpuSolution++;
239
336
  if (cpuSolution % 25000 === 0) {
240
337
  self.postMessage({ type: 'progress', solution: cpuSolution });
@@ -269,22 +366,17 @@ export async function solveCpuTargetInline(baseBlock, target, progressCallback)
269
366
  while (true) {
270
367
  const solutionBytes = encoder.encode(String(cpuSolution));
271
368
 
272
- // Concaténation binaire directe : c'est plus rapide et plus sûr.
369
+ // Direct binary concatenation: faster and safer.
273
370
  const finalBlock = new Uint8Array(baseBlock.length + solutionBytes.length);
274
371
  finalBlock.set(baseBlock);
275
372
  finalBlock.set(solutionBytes, baseBlock.length);
276
373
 
277
- if (cpuSolution === 0) {
278
- const reconstructedMsg = new TextDecoder().decode(finalBlock);
279
- console.log('[FP Solve Debug] Client will hash message:', reconstructedMsg);
280
- }
281
- const buf = await crypto.subtle.digest("SHA-256", finalBlock);
282
- const hashHex = Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2, '0')).join('');
283
- // --- AJOUT DE LOGS POUR LE DÉBOGAGE CÔTÉ CLIENT ---
374
+ const hashHex = await safeDigestSha256Hex(finalBlock);
375
+ // --- CLIENT-SIDE DEBUG LOGS ---
284
376
  if (cpuSolution === 0) { // Log only the first attempt
285
377
  console.log(`[FP Client Solve] Attempt 0 hash: "0x${hashHex}"`);
286
378
  }
287
- // --- FIN DES LOGS ---
379
+ // --- END DEBUG LOGS ---
288
380
  if (BigInt('0x' + hashHex) < cpuTarget) break;
289
381
  cpuSolution++;
290
382
  if (cpuSolution % 50000 === 0) {
@@ -300,28 +392,28 @@ export async function solveCpuTargetInline(baseBlock, target, progressCallback)
300
392
  }
301
393
 
302
394
  /**
303
- * Résout un challenge CPU basé sur une cible (version Web Worker).
304
- * @param {string} message - Le message à hasher (ex: `ip:nonce:solution:secret`).
305
- * @param {bigint} target - La cible à atteindre.
306
- * @returns {Promise<number>} La solution (un nombre entier).
395
+ * Solves a target-based CPU challenge (Web Worker version).
396
+ * @param {string} message - Message to hash (e.g. `ip:nonce:solution:secret`).
397
+ * @param {bigint} target - Target difficulty to reach.
398
+ * @returns {Promise<number>} Solution integer.
307
399
  */
308
400
  export async function solveCpuTarget(message, target) {
309
401
  // Vérifie si les Web Workers sont supportés par le navigateur.
310
402
  if (typeof(Worker) === "undefined") {
311
403
  console.warn("Web Workers not supported. Falling back to main thread calculation (UI may freeze).");
312
- // Ici, on pourrait remettre l'ancienne implémentation comme solution de secours.
313
- // Pour la clarté, nous supposons que les workers sont disponibles.
404
+ // An inline fallback implementation could be placed here if needed.
405
+ // For clarity, we assume worker availability.
314
406
  throw new Error("Web Worker support is required for CPU challenges.");
315
407
  }
316
408
 
317
409
  return new Promise((resolve, reject) => {
318
- // Crée un worker à partir du script dédié. Le chemin doit être accessible publiquement.
319
- // Assurez-vous que `pow.worker.js` est servi par votre serveur statique.
410
+ // Create a worker from the dedicated script. The path must be publicly accessible.
411
+ // Ensure `pow.worker.js` is served by your static assets server.
320
412
  const worker = new Worker('./pow.worker.js');
321
413
 
322
414
  worker.onmessage = (event) => {
323
415
  resolve(event.data.solution);
324
- worker.terminate(); // Nettoie le worker une fois le travail terminé.
416
+ worker.terminate(); // Clean up worker once finished.
325
417
  };
326
418
 
327
419
  worker.onerror = (error) => {
@@ -329,16 +421,16 @@ export async function solveCpuTarget(message, target) {
329
421
  worker.terminate();
330
422
  };
331
423
 
332
- // Envoie les données du challenge au worker pour qu'il commence le calcul.
424
+ // Send challenge data to the worker to start calculation.
333
425
  worker.postMessage({ message, target });
334
426
  });
335
427
  }
336
428
 
337
429
  /**
338
- * Résout un challenge basé sur la mémoire.
339
- * @param {string} seed - La graine pour l'initialisation de la mémoire.
340
- * @param {number} difficulty - La difficulté (en Mo).
341
- * @returns {Promise<number>} La solution (nombre entier).
430
+ * Solves a memory-hard challenge.
431
+ * @param {string} seed - Seed for memory initialization.
432
+ * @param {number} difficulty - Difficulty in MB.
433
+ * @returns {Promise<number>} Solution integer.
342
434
  */
343
435
  export async function solveMemory(seed, difficulty) {
344
436
  const wasmModule = typeof window !== 'undefined' ? (window.wasmModule || (window.ClientLibrary && window.ClientLibrary.wasmModule)) : null;
@@ -360,8 +452,7 @@ export async function solveMemory(seed, difficulty) {
360
452
  if (numBlocks === 0) return { solution: 0, merkleRoot: '', proofs: {} };
361
453
 
362
454
  async function hashBlock(block) {
363
- const buf = await crypto.subtle.digest("SHA-256", block.buffer);
364
- return Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2, '0')).join('');
455
+ return await safeDigestSha256Hex(block.buffer);
365
456
  }
366
457
 
367
458
  function hexToBytes(hex) {
@@ -392,8 +483,7 @@ export async function solveMemory(seed, difficulty) {
392
483
  const left = currentLayer[i];
393
484
  const right = currentLayer[i + 1] || left;
394
485
  const combined = hexToBytes(left + right);
395
- const hashBuf = await crypto.subtle.digest("SHA-256", combined);
396
- const hashHex = Array.from(new Uint8Array(hashBuf)).map(b => b.toString(16).padStart(2, '0')).join('');
486
+ const hashHex = await safeDigestSha256Hex(combined);
397
487
  nextLayer.push(hashHex);
398
488
  }
399
489
  tree.push(nextLayer);
@@ -442,12 +532,12 @@ export async function solveMemory(seed, difficulty) {
442
532
  }
443
533
 
444
534
  /**
445
- * Résout un challenge de type "Problème du Voyageur de Commerce" (TSP).
446
- * NOTE: Ceci est une implémentation simple (heuristique du plus proche voisin) et n'est pas garantie
447
- * de trouver la solution optimale, mais elle est suffisante pour un challenge.
448
- * @param {Array<{x: number, y: number}>} cities - Les coordonnées des villes.
449
- * @param {number} targetMaxDistance - La distance maximale acceptable.
450
- * @returns {Promise<{path: number[], distance: number}>} Le chemin et la distance.
535
+ * Solves a Traveling Salesperson Problem (TSP) challenge.
536
+ * NOTE: Uses a nearest-neighbor heuristic which may not find the global optimum,
537
+ * but is sufficient for verification challenges.
538
+ * @param {Array<{x: number, y: number}>} cities - City coordinates.
539
+ * @param {number} targetMaxDistance - Maximum acceptable distance.
540
+ * @returns {Promise<{path: number[], distance: number}>} Tour path and distance.
451
541
  */
452
542
  export async function solveTsp(cities, targetMaxDistance) {
453
543
  // Utility function to calculate the distance between two cities
@@ -505,7 +595,7 @@ export async function solveTsp(cities, targetMaxDistance) {
505
595
  return { path: solutionPath, distance: solutionDistance };
506
596
  }
507
597
 
508
- // --- Fonctions d'optimisation copiées/adaptées de library.js pour le client ---
598
+ // --- Optimization functions adapted from library.js for client usage ---
509
599
 
510
600
  function paretoDominates(objectivesA, objectivesB) {
511
601
  let aIsBetterInOne = false;
@@ -853,9 +943,9 @@ const ClientOptimizers = {
853
943
  };
854
944
 
855
945
  /**
856
- * Résout une unité de travail utile (Useful Work Unit).
857
- * @param {object} task - La tâche envoyée par le serveur.
858
- * @returns {Promise<object>} Le résultat du calcul.
946
+ * Solves a Useful Work Unit (uPoW).
947
+ * @param {object} task - Task payload sent by the server.
948
+ * @returns {Promise<object>} Computation result.
859
949
  */
860
950
  async function solveUsefulWorkTask(task) {
861
951
  await new Promise(r => setTimeout(r, 10)); // Yield thread
@@ -930,7 +1020,7 @@ async function solveUsefulWorkTask(task) {
930
1020
  const crossover = (p1, p2) => p1.map((w, i) => (w + p2[i]) / 2);
931
1021
  const mutate = p => { const n = [...p], i = Math.floor(secureRandom() * n.length); n[i] += (secureRandom() - 0.5) * 0.2; return n.map(v => Math.max(0, v)); };
932
1022
 
933
- // Le client doit recréer la population si elle n'est pas fournie
1023
+ // The client must recreate the population if not provided
934
1024
  const initialPopulation = task.initialPopulation || Array.from({ length: task.payload.options.populationSize }, () => ({ chromosome: createIndividual(), fitness: 0 }));
935
1025
  initialPopulation.forEach(p => p.fitness = fitness(p.chromosome));
936
1026
 
@@ -939,33 +1029,31 @@ async function solveUsefulWorkTask(task) {
939
1029
  }
940
1030
 
941
1031
  case 'run_multiple_parallel':
942
- // Côté client, on ne peut pas utiliser de vrais workers pour `runMultipleParallel`.
943
- // On exécute donc une version simplifiée : un seul cycle du solveur demandé.
944
- // Cela reste un travail coûteux et valide le principe du "Useful Work".
1032
+ // On the client side, Web Workers are not nested for runMultipleParallel.
1033
+ // Run a single cycle of the requested solver.
1034
+ // This still represents non-trivial computational work for useful PoW.
945
1035
  const { solverName, baseSolverArgs } = task;
946
1036
  const clientSolver = ClientOptimizers[solverName];
947
1037
  if (!clientSolver) throw new Error(`Solver ${solverName} not found on client.`);
948
1038
 
949
- // On simule l'appel avec les arguments de base.
950
- // Note: `baseSolverArgs` peut contenir des options.
1039
+ // Execute solver with base arguments.
1040
+ // Note: `baseSolverArgs` may contain solver options.
951
1041
  return clientSolver(...baseSolverArgs);
952
1042
 
953
1043
  case 'multi_objective_genetic_algorithm': {
954
- // C'est ici que la connexion se fait !
955
- // On cherche le solveur demandé (ex: 'cpc.solve') dans notre registre client.
1044
+ // Look up requested solver (e.g. 'cpc.solve') in the client registry.
956
1045
  const solverFunction = ClientOptimizers[task.solverName];
957
1046
  if (!solverFunction) {
958
1047
  throw new Error(`Solver '${task.solverName}' not found on client.`);
959
1048
  }
960
1049
  // On appelle le solveur en lui passant le payload et les options.
961
- // La fonction `solveOptimalCPC` attend le payload comme premier argument.
1050
+ // Invoke solver with payload and options.
962
1051
  return solverFunction(task.payload, { generations: task.generations, initialFront: task.initialFront });
963
1052
  }
964
1053
 
965
1054
  case 'pytorch_onnx_learning': {
966
- // Dans un environnement de production réel avec ONNX Runtime Web, on chargerait le modèle .onnx
967
- // et on exécuterait un calcul de gradient (backward pass).
968
- // Ici, nous simulons le calcul de gradient mathématique équivalent pour la régression/classification.
1055
+ // In a production environment with ONNX Runtime Web, load .onnx and execute gradient backward pass.
1056
+ // Here, simulate the gradient backward pass for regression/classification.
969
1057
  const { weights, payload } = task;
970
1058
  const inputs = payload.inputs || [];
971
1059
  const labels = payload.labels || [];
@@ -991,7 +1079,7 @@ async function solveUsefulWorkTask(task) {
991
1079
  const labels = payload.labels || [];
992
1080
  try {
993
1081
  const tf = await loadTfjs();
994
- // Chargement non-bloquant du modèle au format JSON
1082
+ // Non-blocking model loading
995
1083
  const model = await tf.loadLayersModel(modelPath);
996
1084
 
997
1085
  const xs = tf.tensor2d(inputs);
@@ -1003,7 +1091,7 @@ async function solveUsefulWorkTask(task) {
1003
1091
  return tf.losses.meanSquaredError(ys, preds);
1004
1092
  };
1005
1093
 
1006
- // Dérivation automatique pour le calcul exact des gradients
1094
+ // Automatic differentiation for exact gradient computation
1007
1095
  const gFn = tf.grad(lossFn);
1008
1096
  const grads = gFn(trainableVars);
1009
1097
 
@@ -1034,15 +1122,15 @@ async function solveUsefulWorkTask(task) {
1034
1122
  }
1035
1123
 
1036
1124
  /**
1037
- * Résout une tâche d'optimisation basée sur un algorithme génétique.
1038
- * Reçoit une population et la fait évoluer pendant un certain nombre de générations.
1039
- * NOTE: Cette fonction est une version simplifiée de l'AG de `library.js` adaptée au client.
1040
- * @param {Array<object>} initialPopulation - La population de départ.
1041
- * @param {number} generations - Le nombre de générations à exécuter.
1042
- * @returns {Promise<Array<object>>} La population finale après évolution.
1125
+ * Solves an optimization task using a genetic algorithm.
1126
+ * Evolves a population for a specified number of generations.
1127
+ * NOTE: Simplified client adaptation of GA in `library.js`.
1128
+ * @param {Array<object>} initialPopulation - Starting population.
1129
+ * @param {number} generations - Number of generations to run.
1130
+ * @returns {Promise<Array<object>>} Final evolved population.
1043
1131
  */
1044
1132
  export async function solveOptimizationTask(initialPopulation, generations) {
1045
- // Fonctions AG simplifiées (croisement, mutation)
1133
+ // Simplified GA operators (crossover, mutation)
1046
1134
  const crossover = (p1, p2) => p1.map((w, i) => (w + p2[i]) / 2);
1047
1135
  const mutate = (p) => {
1048
1136
  const newP = [...p];
@@ -1054,20 +1142,20 @@ export async function solveOptimizationTask(initialPopulation, generations) {
1054
1142
  let population = initialPopulation;
1055
1143
 
1056
1144
  for (let gen = 0; gen < generations; gen++) {
1057
- // Sélection simple : on garde les 50% meilleurs
1145
+ // Simple selection: keep top 50%
1058
1146
  const parents = population.sort((a, b) => a.fitness - b.fitness).slice(0, Math.ceil(population.length / 2));
1059
- const newPopulation = [...parents]; // Élitisme
1147
+ const newPopulation = [...parents]; // Elitism
1060
1148
 
1061
1149
  while (newPopulation.length < population.length) {
1062
1150
  const parent1 = parents[Math.floor(secureRandom() * parents.length)];
1063
1151
  const parent2 = parents[Math.floor(secureRandom() * parents.length)];
1064
1152
  let offspring = crossover(parent1.chromosome, parent2.chromosome);
1065
1153
  if (secureRandom() < 0.1) offspring = mutate(offspring);
1066
- // La fitness sera recalculée côté serveur pour la vérification.
1154
+ // Fitness is recalculated server-side for verification.
1067
1155
  newPopulation.push({ chromosome: offspring, fitness: -1 });
1068
1156
  }
1069
1157
  population = newPopulation;
1070
- // Pause pour ne pas geler l'UI sur les longues tâches
1158
+ // Yield execution to prevent freezing the UI on long tasks
1071
1159
  if (gen % 10 === 0) await new Promise(r => setTimeout(r, 0));
1072
1160
  }
1073
1161
  return population;
@@ -1075,7 +1163,7 @@ export async function solveOptimizationTask(initialPopulation, generations) {
1075
1163
 
1076
1164
  /**
1077
1165
  * @class ChallengeSolution
1078
- * @description Encapsule une solution de challenge et fournit des méthodes pour la manipuler.
1166
+ * @description Encapsulates a challenge solution and formatting helpers.
1079
1167
  * @private
1080
1168
  */
1081
1169
  class ChallengeSolution {
@@ -1086,14 +1174,14 @@ class ChallengeSolution {
1086
1174
  }
1087
1175
 
1088
1176
  /**
1089
- * Applique les paramètres de la solution à un objet URL.
1090
- * @param {URL} url - L'objet URL à modifier.
1177
+ * Applies solution parameters to a URL instance.
1178
+ * @param {URL} url - Target URL object to modify.
1091
1179
  */
1092
1180
  applyToUrl(url) {
1093
1181
  url.searchParams.set('pow_type', this.type);
1094
1182
  url.searchParams.set('pow_nonce', this.nonce);
1095
1183
 
1096
- // Logique de formatage spécifique à chaque type de challenge
1184
+ // Challenge-specific parameter formatting
1097
1185
  if (this.type === 'cpu_mem' || this.type === 'cpu_mem_inline' || this.type === 'cpu_target') {
1098
1186
  Object.entries(this.rawSolution).forEach(([key, value]) => {
1099
1187
  if (typeof value === 'object' && value !== null) {
@@ -1106,17 +1194,17 @@ class ChallengeSolution {
1106
1194
  url.searchParams.set('pow_solution_work_result', JSON.stringify(this.rawSolution.work_result));
1107
1195
  url.searchParams.set('pow_problem_id', this.rawSolution.problem_id);
1108
1196
  } else {
1109
- // Pour les cas simples comme 'tsp' où la solution est une seule valeur
1197
+ // Single-value solutions like TSP
1110
1198
  url.searchParams.set('pow_solution', JSON.stringify(this.rawSolution));
1111
1199
  }
1112
1200
  }
1113
1201
  }
1114
1202
 
1115
1203
  /**
1116
- * Fonction principale qui reçoit un objet challenge et le résout.
1117
- * @param {object} challenge - L'objet challenge reçu du serveur.
1118
- * @param {string} [fingerprint=''] - L'empreinte de l'appareil qui résout le challenge.
1119
- * @returns {Promise<ChallengeSolution>} Un objet `ChallengeSolution` encapsulant le résultat.
1204
+ * Main entry point to resolve a received challenge payload.
1205
+ * @param {object} challenge - Challenge payload received from the server.
1206
+ * @param {string} [fingerprint=''] - Device fingerprint of the solver.
1207
+ * @returns {Promise<ChallengeSolution>} `ChallengeSolution` wrapping the result.
1120
1208
  */
1121
1209
  export async function solveChallenge(challenge, fingerprint = '') { // The fingerprint is now passed from the client library
1122
1210
  const { type, nonce, clientSecret, cpuTarget, memDifficulty, cities, clientIp, targetMaxDistance, optimizationTask, usefulWorkTask, queries, sizeMb } = challenge;
@@ -1129,12 +1217,12 @@ export async function solveChallenge(challenge, fingerprint = '') { // The finge
1129
1217
  throw new Error("Challenge data is missing 'cpuTarget' property.");
1130
1218
  }
1131
1219
  const target = cpuTarget; // Keep variable name for consistency below
1132
- // Pour ce challenge simple, le baseBlock est juste le nonce.
1220
+ // For this simple challenge, baseBlock is the nonce.
1133
1221
  const baseBlockBytes = new TextEncoder().encode(nonce + ":");
1134
1222
  rawSolution.cpu = await solveCpuTargetInline(baseBlockBytes, target, null);
1135
1223
  break;
1136
1224
  case 'cpu_mem':
1137
- // Pour les appels API, le client IP n'est pas connu, on ne le met pas dans le message
1225
+ // For API calls, client IP is omitted from client-side message reconstruction
1138
1226
  const baseMessageCombined = `:${nonce}:${clientSecret}`;
1139
1227
  const memSeed = `:${nonce}:${clientSecret}`;
1140
1228
  const [cpuSol, memSol] = await Promise.all([
@@ -1149,7 +1237,7 @@ export async function solveChallenge(challenge, fingerprint = '') { // The finge
1149
1237
  rawSolution.mem = memSol;
1150
1238
  break;
1151
1239
  case 'cpu_mem_inline':
1152
- // Version inline pour compatibilité HTML avec IP incluse
1240
+ // Inline version for HTML compatibility with IP
1153
1241
  const memSeedInline = `:${nonce}:${clientSecret}`;
1154
1242
  const [cpuSolInline, memSolInline] = await Promise.all([
1155
1243
  (async () => {
@@ -1164,12 +1252,12 @@ export async function solveChallenge(challenge, fingerprint = '') { // The finge
1164
1252
  break;
1165
1253
  case 'tsp':
1166
1254
  const tspResult = await solveTsp(cities, targetMaxDistance);
1167
- // Pour ce challenge, la solution est juste le chemin.
1255
+ // For TSP, solution is the tour path array
1168
1256
  rawSolution = tspResult.path;
1169
1257
  break;
1170
1258
  case 'optimization_task':
1171
1259
  const finalPopulation = await solveOptimizationTask(optimizationTask.population, optimizationTask.generations);
1172
- rawSolution = finalPopulation.map(p => p.chromosome); // On ne renvoie que les chromosomes
1260
+ rawSolution = finalPopulation.map(p => p.chromosome); // Return chromosomes only
1173
1261
  break;
1174
1262
  case 'useful_work_task':
1175
1263
  const workResult = await solveUsefulWorkTask(usefulWorkTask.task);
@@ -1187,8 +1275,8 @@ export async function solveChallenge(challenge, fingerprint = '') { // The finge
1187
1275
  return new ChallengeSolution(type, nonce, rawSolution);
1188
1276
  }
1189
1277
 
1190
- // --- Compatibilité pour l'injection directe dans le HTML ---
1191
- // Si le script est chargé dans un navigateur (window existe), on attache les fonctions nécessaires à window.
1278
+ // --- Direct HTML script injection compatibility ---
1279
+ // If loaded in browser context (window exists), expose methods on window.
1192
1280
  if (typeof window !== 'undefined') {
1193
1281
  window.solveCpuChallengeInline = solveCpuTargetInline;
1194
1282
  window.solveMemoryChallenge = solveMemory;
@@ -1,49 +1,49 @@
1
- /**
2
- * @file @/pow.worker.js
3
- * @description Web Worker dédié à la résolution du challenge CPU.
4
- * Ce script s'exécute sur un thread séparé pour ne pas bloquer l'interface utilisateur.
5
- */
6
-
7
- self.onmessage = async (event) => {
8
- const { message, baseBlock, target } = event.data;
9
- const cpuTarget = BigInt(target);
10
- let solution = 0;
11
- const encoder = new TextEncoder();
12
- if (baseBlock) {
13
- // Mode solveCpuTargetInline (Uint8Array base block)
14
- const blockArray = new Uint8Array(baseBlock);
15
- while (true) {
16
- const solutionBytes = encoder.encode(String(solution));
17
- const finalBlock = new Uint8Array(blockArray.length + solutionBytes.length);
18
- finalBlock.set(blockArray);
19
- finalBlock.set(solutionBytes, blockArray.length);
20
- const hashBuffer = await crypto.subtle.digest('SHA-256', finalBlock);
21
- const hashArray = Array.from(new Uint8Array(hashBuffer));
22
- const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
23
-
24
- if (BigInt('0x' + hashHex) < cpuTarget) {
25
- self.postMessage({ type: 'success', solution });
26
- return;
27
- }
28
- solution++;
29
- if (solution % 25000 === 0) {
30
- self.postMessage({ type: 'progress', solution });
31
- }
32
- }
33
- } else {
34
- // Mode solveCpuTarget standard/legacy (Message string)
35
- while (true) {
36
- const currentMessage = `${message}:${solution}`;
37
- const data = encoder.encode(currentMessage);
38
- const hashBuffer = await crypto.subtle.digest('SHA-256', data);
39
- const hashArray = Array.from(new Uint8Array(hashBuffer));
40
- const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
41
-
42
- if (BigInt('0x' + hashHex) < cpuTarget) {
43
- self.postMessage({ solution });
44
- return;
45
- }
46
- solution++;
47
- }
48
- }
1
+ /**
2
+ * @file @/pow.worker.js
3
+ * @description Dedicated Web Worker for CPU challenge solving.
4
+ * Executes on a separate thread to avoid blocking the main UI thread.
5
+ */
6
+
7
+ self.onmessage = async (event) => {
8
+ const { message, baseBlock, target } = event.data;
9
+ const cpuTarget = BigInt(target);
10
+ let solution = 0;
11
+ const encoder = new TextEncoder();
12
+ if (baseBlock) {
13
+ // Mode solveCpuTargetInline (Uint8Array base block)
14
+ const blockArray = new Uint8Array(baseBlock);
15
+ while (true) {
16
+ const solutionBytes = encoder.encode(String(solution));
17
+ const finalBlock = new Uint8Array(blockArray.length + solutionBytes.length);
18
+ finalBlock.set(blockArray);
19
+ finalBlock.set(solutionBytes, blockArray.length);
20
+ const hashBuffer = await crypto.subtle.digest('SHA-256', finalBlock);
21
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
22
+ const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
23
+
24
+ if (BigInt('0x' + hashHex) < cpuTarget) {
25
+ self.postMessage({ type: 'success', solution });
26
+ return;
27
+ }
28
+ solution++;
29
+ if (solution % 25000 === 0) {
30
+ self.postMessage({ type: 'progress', solution });
31
+ }
32
+ }
33
+ } else {
34
+ // Mode solveCpuTarget standard/legacy (Message string)
35
+ while (true) {
36
+ const currentMessage = `${message}:${solution}`;
37
+ const data = encoder.encode(currentMessage);
38
+ const hashBuffer = await crypto.subtle.digest('SHA-256', data);
39
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
40
+ const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
41
+
42
+ if (BigInt('0x' + hashHex) < cpuTarget) {
43
+ self.postMessage({ solution });
44
+ return;
45
+ }
46
+ solution++;
47
+ }
48
+ }
49
49
  };