@anonympins/fingerprint 0.6.7 → 0.7.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/CHANGELOG.md +33 -0
- package/README.md +3 -7
- package/composer.json +8 -1
- package/package.json +1 -1
- package/public/fingerprint-wordpress.zip +0 -0
- package/public/fp.wasm +0 -0
- package/src/js/build-client.js +9 -11
- package/src/js/dynamic-wasm.js +7 -7
- package/src/js/fingerprint.builder.js +37 -38
- package/src/js/fingerprint.client.js +236 -152
- package/src/js/fingerprint.client.obfuscated.js +1 -1
- package/src/js/fingerprint.js +517 -101
- package/src/js/fingerprint.utils.js +213 -213
- package/src/js/library.js +355 -384
- package/src/js/mongodb-store.js +1 -1
- package/src/js/optimization.worker.js +27 -27
- package/src/js/pow.solver.inline.js +206 -62
- package/src/js/pow.solver.js +235 -101
- package/src/js/pow.worker.js +48 -26
- package/src/js/redis-store.js +1 -1
- package/src/js/tests/fingerprint.client.init.test.js +146 -143
- package/src/js/tests/fingerprint.test.js +101 -40
- package/src/js/tests/ip-reputation.test.js +1 -0
- package/src/js/upow-model-task.js +14 -15
- package/src/php/AutoTuner.php +34 -34
- package/src/php/Challenge/ChallengeUtils.php +204 -32
- package/src/php/Config/SecurityProfiles.php +21 -0
- package/src/php/DirectFingerprint.php +190 -145
- package/src/php/FingerprintBuilder.php +29 -30
- package/src/php/FingerprintClient.php +24 -25
- package/src/php/FingerprintEngine.php +1836 -1880
- package/src/php/Ja3AnomalyDetector.php +258 -227
- package/src/php/Optimization/FunctionRegistry.php +7 -7
- package/src/php/Optimization/Optimization.php +24 -24
- package/src/php/Optimization/OptimizationOperators.php +28 -27
- package/src/php/Optimization/ProblemInitializers.php +52 -52
- package/src/php/ProblemManager.php +31 -34
- package/src/php/RequestContext.php +8 -1
- package/src/php/Store/IStore.php +7 -8
- package/src/php/Store/InMemoryStore.php +66 -66
- package/src/php/Store/MongoDbStore.php +5 -5
- package/src/php/Store/RedisStore.php +5 -5
- package/src/php/Store/StoreManager.php +35 -35
- package/src/php/Tests/ChallengeUtilsTest.php +453 -453
- package/src/php/Tests/DnsCircuitBreakerTest.php +105 -105
- package/src/php/Tests/FingerprintEngineTest.php +662 -693
- package/src/php/Tests/IpReputationTest.php +10 -0
- package/src/php/Tests/MaliciousPatternsTest.php +103 -103
- package/src/php/Tests/MetricsTest.php +97 -97
- package/src/php/Tests/ProblemManagerTest.php +376 -376
- package/src/php/Tests/QuicFingerprintTest.php +53 -53
- package/src/php/Tests/RequestUtilsTest.php +471 -471
- package/src/php/Tests/TLSClientHelloParserTest.php +177 -177
- package/src/php/Tests/config/ed25519_key.json +3 -3
- package/src/php/Utils/Env.php +49 -49
- package/src/php/Utils/MaliciousPatterns.php +74 -74
- package/src/php/Utils/MetricsManager.php +209 -209
- package/src/php/Utils/RequestUtils.php +236 -19
- package/src/php/Utils/TLSClientHelloParser.php +369 -212
- package/src/php/WordPress/WpDbStore.php +155 -0
- package/src/php/WordPress/fingerprint-wordpress.php +217 -0
- package/src/php/WordPress/package.php +123 -0
- package/src/php/bin/auto-tune.php +118 -118
package/src/js/mongodb-store.js
CHANGED
|
@@ -49,7 +49,7 @@ export function createMongoDbStore(db, collectionName = 'fingerprint_store') {
|
|
|
49
49
|
reconnecting = false;
|
|
50
50
|
clearInterval(interval);
|
|
51
51
|
} catch (err) {
|
|
52
|
-
//
|
|
52
|
+
// Connection still down, continue retry loop
|
|
53
53
|
}
|
|
54
54
|
}, 5000);
|
|
55
55
|
if (interval.unref) interval.unref();
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file @/optimization.worker.js
|
|
3
|
-
* @description Web Worker
|
|
4
|
-
*
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import {parentPort, workerData} from 'worker_threads';
|
|
8
|
-
import {Optimization} from './library.js';
|
|
9
|
-
|
|
10
|
-
if (parentPort) {
|
|
11
|
-
parentPort.on('message', async () => { //
|
|
12
|
-
const { solverName, solverArgs } = workerData;
|
|
13
|
-
|
|
14
|
-
//
|
|
15
|
-
const solverFunction = solverName.split('.').reduce((obj, prop) => obj && obj[prop], Optimization);
|
|
16
|
-
|
|
17
|
-
if (typeof solverFunction === 'function') {
|
|
18
|
-
try {
|
|
19
|
-
const result = await solverFunction(...solverArgs);
|
|
20
|
-
parentPort.postMessage(result);
|
|
21
|
-
} catch (error) {
|
|
22
|
-
parentPort.postMessage({ error: error.message, stack: error.stack });
|
|
23
|
-
}
|
|
24
|
-
} else {
|
|
25
|
-
parentPort.postMessage({ error: `Solver '${solverName}' not found or is not a function in Optimization library.` });
|
|
26
|
-
}
|
|
27
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* @file @/optimization.worker.js
|
|
3
|
+
* @description Generic Web Worker running optimization routines from `Optimization`.
|
|
4
|
+
* Runs on a dedicated worker thread to maintain main thread responsiveness.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {parentPort, workerData} from 'worker_threads';
|
|
8
|
+
import {Optimization} from './library.js';
|
|
9
|
+
|
|
10
|
+
if (parentPort) {
|
|
11
|
+
parentPort.on('message', async () => { // Empty message payload; parameters loaded via workerData
|
|
12
|
+
const { solverName, solverArgs } = workerData;
|
|
13
|
+
|
|
14
|
+
// Resolve nested solver paths (e.g. 'Operators.solvePortfolio')
|
|
15
|
+
const solverFunction = solverName.split('.').reduce((obj, prop) => obj && obj[prop], Optimization);
|
|
16
|
+
|
|
17
|
+
if (typeof solverFunction === 'function') {
|
|
18
|
+
try {
|
|
19
|
+
const result = await solverFunction(...solverArgs);
|
|
20
|
+
parentPort.postMessage(result);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
parentPort.postMessage({ error: error.message, stack: error.stack });
|
|
23
|
+
}
|
|
24
|
+
} else {
|
|
25
|
+
parentPort.postMessage({ error: `Solver '${solverName}' not found or is not a function in Optimization library.` });
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
28
|
}
|
|
@@ -1,20 +1,107 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file @/pow.solver.inline.js
|
|
3
|
-
* @description
|
|
4
|
-
*
|
|
3
|
+
* @description Client-side functions to solve various Proof-of-Work challenges.
|
|
4
|
+
* Compatible with direct HTML script injection (no `export`).
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
9
|
function secureRandom() {
|
|
10
|
-
if (typeof
|
|
10
|
+
if (typeof globalThis !== 'undefined' && globalThis.crypto && globalThis.crypto.getRandomValues) {
|
|
11
11
|
const array = new Uint32Array(1);
|
|
12
|
-
|
|
13
|
-
return array[0] /
|
|
12
|
+
globalThis.crypto.getRandomValues(array);
|
|
13
|
+
return array[0] / 0xffffffff;
|
|
14
14
|
}
|
|
15
15
|
return Math.random();
|
|
16
16
|
}
|
|
17
|
+
function sha256Sync(bytes) {
|
|
18
|
+
if (bytes instanceof ArrayBuffer) {
|
|
19
|
+
bytes = new Uint8Array(bytes);
|
|
20
|
+
} else if (typeof bytes === 'string') {
|
|
21
|
+
bytes = new TextEncoder().encode(bytes);
|
|
22
|
+
} else if (ArrayBuffer.isView(bytes) && !(bytes instanceof Uint8Array)) {
|
|
23
|
+
bytes = new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
var l = bytes.length;
|
|
27
|
+
var bitLen = l * 8;
|
|
28
|
+
var K = new Uint32Array([
|
|
29
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
|
30
|
+
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
|
31
|
+
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76ca1422,
|
|
32
|
+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
|
33
|
+
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
|
34
|
+
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
35
|
+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
|
36
|
+
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
|
37
|
+
]);
|
|
38
|
+
var H = new Uint32Array([
|
|
39
|
+
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
|
40
|
+
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
// Standard SHA-256 padding
|
|
44
|
+
var padLen = (((l + 8) >> 6) + 1) << 6;
|
|
45
|
+
var buf = new Uint8Array(padLen);
|
|
46
|
+
buf.set(bytes);
|
|
47
|
+
buf[l] = 0x80;
|
|
48
|
+
|
|
49
|
+
var view = new DataView(buf.buffer);
|
|
50
|
+
// 64-bit big-endian length in bits (padLen - 8)
|
|
51
|
+
var highBitLen = Math.floor(bitLen / 0x100000000);
|
|
52
|
+
view.setUint32(padLen - 8, highBitLen >>> 0);
|
|
53
|
+
view.setUint32(padLen - 4, bitLen >>> 0);
|
|
54
|
+
|
|
55
|
+
var W = new Int32Array(64);
|
|
56
|
+
for (var i = 0; i < padLen; i += 64) {
|
|
57
|
+
for (var t = 0; t < 16; t++) {
|
|
58
|
+
// Explicit big-endian read (false)
|
|
59
|
+
W[t] = view.getInt32(i + t * 4, false);
|
|
60
|
+
}
|
|
61
|
+
for (var t = 16; t < 64; t++) {
|
|
62
|
+
var s0 = ((W[t - 15] >>> 7) | (W[t - 15] << 25)) ^ ((W[t - 15] >>> 18) | (W[t - 15] << 14)) ^ (W[t - 15] >>> 3);
|
|
63
|
+
var s1 = ((W[t - 2] >>> 17) | (W[t - 2] << 15)) ^ ((W[t - 2] >>> 19) | (W[t - 2] << 13)) ^ (W[t - 2] >>> 10);
|
|
64
|
+
W[t] = (W[t - 16] + s0 + W[t - 7] + s1) | 0;
|
|
65
|
+
}
|
|
66
|
+
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];
|
|
67
|
+
for (var t = 0; t < 64; t++) {
|
|
68
|
+
var S1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7));
|
|
69
|
+
var ch = (e & f) ^ ((~e) & g);
|
|
70
|
+
var temp1 = (h + S1 + ch + K[t] + W[t]) | 0;
|
|
71
|
+
var S0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10));
|
|
72
|
+
var maj = (a & b) ^ (a & c) ^ (b & c);
|
|
73
|
+
var temp2 = (S0 + maj) | 0;
|
|
74
|
+
h = g; g = f; f = e; e = (d + temp1) | 0;
|
|
75
|
+
d = c; c = b; b = a; a = (temp1 + temp2) | 0;
|
|
76
|
+
}
|
|
77
|
+
H[0] = (H[0] + a) | 0;
|
|
78
|
+
H[1] = (H[1] + b) | 0;
|
|
79
|
+
H[2] = (H[2] + c) | 0;
|
|
80
|
+
H[3] = (H[3] + d) | 0;
|
|
81
|
+
H[4] = (H[4] + e) | 0;
|
|
82
|
+
H[5] = (H[5] + f) | 0;
|
|
83
|
+
H[6] = (H[6] + g) | 0;
|
|
84
|
+
H[7] = (H[7] + h) | 0;
|
|
85
|
+
}
|
|
86
|
+
var hex = '';
|
|
87
|
+
for (var i = 0; i < 8; i++) {
|
|
88
|
+
hex += (H[i] >>> 0).toString(16).padStart(8, '0');
|
|
89
|
+
}
|
|
90
|
+
return hex;
|
|
91
|
+
}
|
|
17
92
|
|
|
93
|
+
async function safeDigestSha256Hex(bytes) {
|
|
94
|
+
const cryptoObj = (typeof globalThis !== 'undefined' && globalThis.crypto) ? globalThis.crypto : (typeof window !== 'undefined' ? window.crypto : null);
|
|
95
|
+
if (cryptoObj && cryptoObj.subtle && typeof cryptoObj.subtle.digest === 'function') {
|
|
96
|
+
try {
|
|
97
|
+
const buf = await cryptoObj.subtle.digest("SHA-256", bytes);
|
|
98
|
+
return Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
99
|
+
} catch (e) {
|
|
100
|
+
// Fallback on failure
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return sha256Sync(bytes);
|
|
104
|
+
}
|
|
18
105
|
function cyrb53(str, seed = 0) {
|
|
19
106
|
const safeStr = (typeof str === 'string' ? str : String(str || '')).slice(0, 10000);
|
|
20
107
|
let h1 = 0xdeadbeef ^ seed,
|
|
@@ -53,6 +140,25 @@ function generateBlock(seed, blockIndex, blockSize = 1024) {
|
|
|
53
140
|
return block;
|
|
54
141
|
}
|
|
55
142
|
|
|
143
|
+
async function readSpaceBlock(blockIdx) {
|
|
144
|
+
const db = await openDb();
|
|
145
|
+
const transaction = db.transaction("blocks", "readonly");
|
|
146
|
+
const store = transaction.objectStore("blocks");
|
|
147
|
+
const getReq = store.get(blockIdx);
|
|
148
|
+
return new Promise((resolve, reject) => {
|
|
149
|
+
getReq.onsuccess = () => {
|
|
150
|
+
const block = getReq.result;
|
|
151
|
+
if (block) {
|
|
152
|
+
const hex = Array.from(block).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
153
|
+
resolve(hex);
|
|
154
|
+
} else {
|
|
155
|
+
reject(new Error("Block not found"));
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
getReq.onerror = () => reject(getReq.error);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
56
162
|
async function loadTfjs() {
|
|
57
163
|
if (typeof tf !== 'undefined') return tf;
|
|
58
164
|
if (typeof window !== 'undefined') {
|
|
@@ -149,12 +255,11 @@ async function solveSpaceChallenge(seed, queries, nonce, clientSecret, peerBlock
|
|
|
149
255
|
const finalBlock = new Uint8Array(finalCombined.length + nonceBytes.length);
|
|
150
256
|
finalBlock.set(finalCombined);
|
|
151
257
|
finalBlock.set(nonceBytes, finalCombined.length);
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
258
|
+
|
|
259
|
+
return await safeDigestSha256Hex(finalBlock);
|
|
155
260
|
}
|
|
156
261
|
|
|
157
|
-
// ---
|
|
262
|
+
// --- Optimization functions adapted from library.js for client usage ---
|
|
158
263
|
|
|
159
264
|
function paretoDominates(objectivesA, objectivesB) {
|
|
160
265
|
let aIsBetterInOne = false;
|
|
@@ -475,7 +580,7 @@ const ClientOptimizers = {
|
|
|
475
580
|
|
|
476
581
|
const mutate = (solution) => {
|
|
477
582
|
const newSolution = [...solution];
|
|
478
|
-
const i = Math.floor(
|
|
583
|
+
const i = Math.floor(secureRandom() * 4);
|
|
479
584
|
const mutationFactors = [500, 1000, 0.1, 2];
|
|
480
585
|
newSolution[i] += (secureRandom() - 0.5) * mutationFactors[i];
|
|
481
586
|
return newSolution;
|
|
@@ -717,11 +822,11 @@ async function solveOptimizationTask(initialPopulation, generations) {
|
|
|
717
822
|
}
|
|
718
823
|
|
|
719
824
|
/**
|
|
720
|
-
*
|
|
721
|
-
* @param {Uint8Array} baseBlock -
|
|
722
|
-
* @param {bigint} target -
|
|
723
|
-
* @param {Function} progressCallback -
|
|
724
|
-
* @returns {Promise<number>}
|
|
825
|
+
* Solves a target-based CPU challenge using a binary base block.
|
|
826
|
+
* @param {Uint8Array} baseBlock - Initial data block (nonce, secret, fp) provided by the server.
|
|
827
|
+
* @param {bigint} target - Target difficulty to reach.
|
|
828
|
+
* @param {Function} progressCallback - Progress update callback.
|
|
829
|
+
* @returns {Promise<number>} Solution integer.
|
|
725
830
|
*/
|
|
726
831
|
async function solveCpuTargetInline(baseBlock, target, progressCallback) {
|
|
727
832
|
// --- FIX: Add validation for the target to prevent BigInt conversion errors ---
|
|
@@ -741,33 +846,74 @@ async function solveCpuTargetInline(baseBlock, target, progressCallback) {
|
|
|
741
846
|
const targetStr = typeof target === 'string' ? target : target.toString(16);
|
|
742
847
|
const targetPtr = wasmModule._malloc(targetStr.length + 1);
|
|
743
848
|
for (let i = 0; i < targetStr.length; i++) {
|
|
744
|
-
wasmModule.
|
|
849
|
+
wasmModule.HEAPU8[targetPtr + i] = targetStr.charCodeAt(i);
|
|
850
|
+
}
|
|
851
|
+
wasmModule.HEAPU8[targetPtr + targetStr.length] = 0; // Null-terminator
|
|
852
|
+
try {
|
|
853
|
+
const solution = wasmModule._solve_cpu_target(ptr, len, targetPtr);
|
|
854
|
+
return solution;
|
|
855
|
+
} finally {
|
|
856
|
+
wasmModule._free(ptr);
|
|
857
|
+
wasmModule._free(targetPtr);
|
|
745
858
|
}
|
|
746
|
-
wasmModule.HEAP8[targetPtr + targetStr.length] = 0;
|
|
747
|
-
const solution = wasmModule._solve_cpu_target(ptr, len, targetPtr);
|
|
748
|
-
wasmModule._free(ptr);
|
|
749
|
-
wasmModule._free(targetPtr);
|
|
750
|
-
return solution;
|
|
751
859
|
}
|
|
752
860
|
|
|
753
861
|
// Try Web Worker execution first if supported and not blocked by CSP
|
|
754
862
|
if (typeof window !== 'undefined' && typeof Worker !== 'undefined') {
|
|
755
863
|
try {
|
|
864
|
+
const staticWorkerPath = window.ClientLibrary?.workerPath || (window.ClientConfig && window.ClientConfig.workerPath);
|
|
865
|
+
const sha256FnStr = sha256Sync.toString();
|
|
866
|
+
if (staticWorkerPath) {
|
|
867
|
+
return await new Promise((resolve, reject) => {
|
|
868
|
+
const worker = new Worker(staticWorkerPath);
|
|
869
|
+
worker.onmessage = (event) => {
|
|
870
|
+
if (event.data && event.data.type === 'progress') {
|
|
871
|
+
if (progressCallback) progressCallback(event.data.solution);
|
|
872
|
+
} else if (event.data && event.data.type === 'success') {
|
|
873
|
+
resolve(event.data.solution);
|
|
874
|
+
worker.terminate();
|
|
875
|
+
} else if (event.data && event.data.solution !== undefined) {
|
|
876
|
+
// Fallback for backward compatibility with previous signature
|
|
877
|
+
resolve(event.data.solution);
|
|
878
|
+
worker.terminate();
|
|
879
|
+
}
|
|
880
|
+
};
|
|
881
|
+
worker.onerror = (err) => {
|
|
882
|
+
worker.terminate();
|
|
883
|
+
reject(err);
|
|
884
|
+
};
|
|
885
|
+
worker.postMessage({ baseBlock, target: cpuTarget.toString() });
|
|
886
|
+
});
|
|
887
|
+
}
|
|
756
888
|
return await new Promise((resolve, reject) => {
|
|
757
889
|
const workerCode = `
|
|
758
890
|
self.onmessage = async (e) => {
|
|
759
891
|
const { baseBlock, target } = e.data;
|
|
760
892
|
const cpuTarget = BigInt(target);
|
|
761
893
|
const encoder = new TextEncoder();
|
|
762
|
-
|
|
894
|
+
const hasSubtle = typeof self.crypto !== 'undefined' && self.crypto && self.crypto.subtle && typeof self.crypto.subtle.digest === 'function';
|
|
895
|
+
const sha256Fallback = ${sha256FnStr};
|
|
896
|
+
let cpuSolution = 0;
|
|
763
897
|
while (true) {
|
|
764
898
|
const solutionBytes = encoder.encode(String(cpuSolution));
|
|
765
899
|
const finalBlock = new Uint8Array(baseBlock.length + solutionBytes.length);
|
|
766
900
|
finalBlock.set(baseBlock);
|
|
767
901
|
finalBlock.set(solutionBytes, baseBlock.length);
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
902
|
+
let hashHex = '';
|
|
903
|
+
if (hasSubtle) {
|
|
904
|
+
try {
|
|
905
|
+
const buf = await self.crypto.subtle.digest("SHA-256", finalBlock);
|
|
906
|
+
const arr = new Uint8Array(buf);
|
|
907
|
+
for (let j = 0; j < arr.length; j++) {
|
|
908
|
+
hashHex += arr[j].toString(16).padStart(2, '0');
|
|
909
|
+
}
|
|
910
|
+
} catch (subtleErr) {
|
|
911
|
+
hashHex = sha256Fallback(finalBlock);
|
|
912
|
+
}
|
|
913
|
+
} else {
|
|
914
|
+
hashHex = sha256Fallback(finalBlock);
|
|
915
|
+
}
|
|
916
|
+
if (BigInt('0x' + hashHex) < cpuTarget) break;
|
|
771
917
|
cpuSolution++;
|
|
772
918
|
if (cpuSolution % 25000 === 0) {
|
|
773
919
|
self.postMessage({ type: 'progress', solution: cpuSolution });
|
|
@@ -802,24 +948,23 @@ async function solveCpuTargetInline(baseBlock, target, progressCallback) {
|
|
|
802
948
|
while (true) {
|
|
803
949
|
const solutionBytes = encoder.encode(String(cpuSolution));
|
|
804
950
|
|
|
805
|
-
//
|
|
951
|
+
// Direct binary concatenation: faster and safer.
|
|
806
952
|
const finalBlock = new Uint8Array(baseBlock.length + solutionBytes.length);
|
|
807
953
|
finalBlock.set(baseBlock);
|
|
808
954
|
finalBlock.set(solutionBytes, baseBlock.length);
|
|
809
955
|
|
|
810
956
|
if (cpuSolution === 0) {
|
|
811
|
-
//
|
|
957
|
+
// For debugging, print reconstructed message.
|
|
812
958
|
const reconstructedMsg = new TextDecoder().decode(finalBlock);
|
|
813
959
|
console.log(`[FP Client Solve] Hashing message: "${reconstructedMsg}"`);
|
|
814
960
|
}
|
|
815
961
|
|
|
816
|
-
const
|
|
817
|
-
|
|
818
|
-
// --- AJOUT DE LOGS POUR LE DÉBOGAGE CÔTÉ CLIENT ---
|
|
962
|
+
const hashHex = await safeDigestSha256Hex(finalBlock);
|
|
963
|
+
// --- CLIENT-SIDE DEBUG LOGS ---
|
|
819
964
|
if (cpuSolution === 0) { // Log only the first attempt
|
|
820
965
|
console.log(`[FP Client Solve] Attempt 0 hash: "0x${hashHex}"`);
|
|
821
966
|
}
|
|
822
|
-
// ---
|
|
967
|
+
// --- END DEBUG LOGS ---
|
|
823
968
|
if (BigInt('0x' + hashHex) < cpuTarget) break;
|
|
824
969
|
cpuSolution++;
|
|
825
970
|
if (cpuSolution % 50000 === 0) {
|
|
@@ -835,28 +980,28 @@ async function solveCpuTargetInline(baseBlock, target, progressCallback) {
|
|
|
835
980
|
}
|
|
836
981
|
|
|
837
982
|
/**
|
|
838
|
-
*
|
|
839
|
-
* @param {string} message -
|
|
840
|
-
* @param {bigint} target -
|
|
841
|
-
* @returns {Promise<number>}
|
|
983
|
+
* Solves a target-based CPU challenge (Web Worker version).
|
|
984
|
+
* @param {string} message - Message to hash (e.g. `ip:nonce:solution:secret`).
|
|
985
|
+
* @param {bigint} target - Target difficulty to reach.
|
|
986
|
+
* @returns {Promise<number>} Solution integer.
|
|
842
987
|
*/
|
|
843
988
|
async function solveCpuTarget(message, target) {
|
|
844
989
|
// Vérifie si les Web Workers sont supportés par le navigateur.
|
|
845
990
|
if (typeof(Worker) === "undefined") {
|
|
846
991
|
console.warn("Web Workers not supported. Falling back to main thread calculation (UI may freeze).");
|
|
847
|
-
//
|
|
848
|
-
//
|
|
992
|
+
// An inline fallback implementation could be placed here if needed.
|
|
993
|
+
// For clarity, we assume workers are available.
|
|
849
994
|
throw new Error("Web Worker support is required for CPU challenges.");
|
|
850
995
|
}
|
|
851
996
|
|
|
852
997
|
return new Promise((resolve, reject) => {
|
|
853
|
-
//
|
|
854
|
-
//
|
|
998
|
+
// Create worker from dedicated script. The path must be publicly accessible.
|
|
999
|
+
// Ensure `pow.worker.js` is served by your static file server.
|
|
855
1000
|
const worker = new Worker('./pow.worker.js');
|
|
856
1001
|
|
|
857
1002
|
worker.onmessage = (event) => {
|
|
858
1003
|
resolve(event.data.solution);
|
|
859
|
-
worker.terminate(); //
|
|
1004
|
+
worker.terminate(); // Clean up worker once finished.
|
|
860
1005
|
};
|
|
861
1006
|
|
|
862
1007
|
worker.onerror = (error) => {
|
|
@@ -864,16 +1009,16 @@ async function solveCpuTarget(message, target) {
|
|
|
864
1009
|
worker.terminate();
|
|
865
1010
|
};
|
|
866
1011
|
|
|
867
|
-
//
|
|
1012
|
+
// Send challenge data to worker to begin computation.
|
|
868
1013
|
worker.postMessage({ message, target });
|
|
869
1014
|
});
|
|
870
1015
|
}
|
|
871
1016
|
|
|
872
1017
|
/**
|
|
873
|
-
*
|
|
874
|
-
* @param {string} seed -
|
|
875
|
-
* @param {number} difficulty -
|
|
876
|
-
* @returns {Promise<number>}
|
|
1018
|
+
* Solves a memory-hard challenge.
|
|
1019
|
+
* @param {string} seed - Seed for memory initialization.
|
|
1020
|
+
* @param {number} difficulty - Difficulty in MB.
|
|
1021
|
+
* @returns {Promise<number>} Solution integer.
|
|
877
1022
|
*/
|
|
878
1023
|
async function solveMemory(seed, difficulty) {
|
|
879
1024
|
const wasmModule = typeof window !== 'undefined' ? (window.wasmModule || (window.ClientLibrary && window.ClientLibrary.wasmModule)) : null;
|
|
@@ -895,8 +1040,7 @@ async function solveMemory(seed, difficulty) {
|
|
|
895
1040
|
if (numBlocks === 0) return { solution: 0, merkleRoot: '', proofs: {} };
|
|
896
1041
|
|
|
897
1042
|
async function hashBlock(block) {
|
|
898
|
-
|
|
899
|
-
return Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
1043
|
+
return await safeDigestSha256Hex(block.buffer);
|
|
900
1044
|
}
|
|
901
1045
|
|
|
902
1046
|
function hexToBytes(hex) {
|
|
@@ -927,8 +1071,7 @@ async function solveMemory(seed, difficulty) {
|
|
|
927
1071
|
const left = currentLayer[i];
|
|
928
1072
|
const right = currentLayer[i + 1] || left;
|
|
929
1073
|
const combined = hexToBytes(left + right);
|
|
930
|
-
const
|
|
931
|
-
const hashHex = Array.from(new Uint8Array(hashBuf)).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
1074
|
+
const hashHex = await safeDigestSha256Hex(combined);
|
|
932
1075
|
nextLayer.push(hashHex);
|
|
933
1076
|
}
|
|
934
1077
|
tree.push(nextLayer);
|
|
@@ -977,12 +1120,12 @@ async function solveMemory(seed, difficulty) {
|
|
|
977
1120
|
}
|
|
978
1121
|
|
|
979
1122
|
/**
|
|
980
|
-
*
|
|
981
|
-
* NOTE:
|
|
982
|
-
*
|
|
983
|
-
* @param {Array<{x: number, y: number}>} cities -
|
|
984
|
-
* @param {number} targetMaxDistance -
|
|
985
|
-
* @returns {Promise<{path: number[], distance: number}>}
|
|
1123
|
+
* Solves a Traveling Salesperson Problem (TSP) challenge.
|
|
1124
|
+
* NOTE: Uses a nearest-neighbor heuristic which may not find the global optimum,
|
|
1125
|
+
* but is sufficient for verification challenges.
|
|
1126
|
+
* @param {Array<{x: number, y: number}>} cities - City coordinates.
|
|
1127
|
+
* @param {number} targetMaxDistance - Maximum acceptable distance.
|
|
1128
|
+
* @returns {Promise<{path: number[], distance: number}>} Tour path and distance.
|
|
986
1129
|
*/
|
|
987
1130
|
async function solveTsp(cities, targetMaxDistance) {
|
|
988
1131
|
// Utility function to calculate the distance between two cities
|
|
@@ -1041,9 +1184,9 @@ async function solveTsp(cities, targetMaxDistance) {
|
|
|
1041
1184
|
}
|
|
1042
1185
|
|
|
1043
1186
|
/**
|
|
1044
|
-
*
|
|
1045
|
-
* @param {object} challenge -
|
|
1046
|
-
* @returns {Promise<object>}
|
|
1187
|
+
* Main entry point to resolve a received challenge payload.
|
|
1188
|
+
* @param {object} challenge - Challenge payload received from the server.
|
|
1189
|
+
* @returns {Promise<object>} Object containing solution(s).
|
|
1047
1190
|
*/
|
|
1048
1191
|
async function solveChallenge(challenge, fingerprint = '') { // fingerprint parameter was already here, but unused in some calls
|
|
1049
1192
|
const { type, nonce, clientSecret, cpuTarget, memDifficulty, cities, clientIp, targetMaxDistance, queries, sizeMb } = challenge;
|
|
@@ -1069,7 +1212,7 @@ async function solveChallenge(challenge, fingerprint = '') { // fingerprint para
|
|
|
1069
1212
|
solutions.cpu = await solveCpuTargetInline(baseBlockBytes, target, null);
|
|
1070
1213
|
break;
|
|
1071
1214
|
case 'cpu_mem':
|
|
1072
|
-
//
|
|
1215
|
+
// For API calls, client IP is omitted from client-side message reconstruction
|
|
1073
1216
|
const memSeed = `:${nonce}:${clientSecret}`;
|
|
1074
1217
|
const [cpuSol, memSol] = await Promise.all([
|
|
1075
1218
|
(async () => {
|
|
@@ -1083,7 +1226,7 @@ async function solveChallenge(challenge, fingerprint = '') { // fingerprint para
|
|
|
1083
1226
|
solutions.mem = memSol;
|
|
1084
1227
|
break;
|
|
1085
1228
|
case 'cpu_mem_inline':
|
|
1086
|
-
//
|
|
1229
|
+
// Inline version for HTML compatibility with IP
|
|
1087
1230
|
const memSeedInline = `:${nonce}:${clientSecret}`;
|
|
1088
1231
|
const [cpuSolInline, memSolInline] = await Promise.all([
|
|
1089
1232
|
(async () => {
|
|
@@ -1112,11 +1255,12 @@ async function solveChallenge(challenge, fingerprint = '') { // fingerprint para
|
|
|
1112
1255
|
return solutions;
|
|
1113
1256
|
}
|
|
1114
1257
|
|
|
1115
|
-
// ---
|
|
1116
|
-
//
|
|
1258
|
+
// --- Direct HTML script injection compatibility ---
|
|
1259
|
+
// If loaded in browser context (window exists), expose methods on window.
|
|
1117
1260
|
if (typeof window !== 'undefined') {
|
|
1118
1261
|
window.solveCpuChallengeInline = solveCpuTargetInline;
|
|
1119
1262
|
window.solveMemoryChallenge = solveMemory;
|
|
1263
|
+
window.readSpaceBlock = readSpaceBlock;
|
|
1120
1264
|
window.solveTspChallenge = solveTsp;
|
|
1121
1265
|
window.solveChallenge = solveChallenge;
|
|
1122
1266
|
}
|