@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.
- package/CHANGELOG.md +29 -0
- package/README.md +1 -1
- package/composer.json +8 -1
- package/package.json +1 -1
- package/public/fingerprint-wordpress.zip +0 -0
- package/src/js/build-client.js +8 -10
- package/src/js/fingerprint.builder.js +37 -38
- package/src/js/fingerprint.client.js +1531 -1463
- package/src/js/fingerprint.client.obfuscated.js +1 -0
- package/src/js/fingerprint.js +190 -16
- package/src/js/fingerprint.utils.js +213 -213
- package/src/js/library.js +340 -376
- package/src/js/mongodb-store.js +1 -1
- package/src/js/optimization.worker.js +27 -27
- package/src/js/pow.solver.inline.js +154 -56
- package/src/js/pow.solver.js +180 -92
- package/src/js/pow.worker.js +48 -48
- 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 +124 -10
- 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 +57 -11
- package/src/php/Config/SecurityProfiles.php +20 -0
- package/src/php/DirectFingerprint.php +190 -145
- package/src/php/FingerprintBuilder.php +29 -30
- package/src/php/FingerprintClient.php +147 -148
- package/src/php/FingerprintEngine.php +44 -17
- package/src/php/Ja3AnomalyDetector.php +33 -32
- package/src/php/Optimization/FunctionRegistry.php +7 -7
- package/src/php/Optimization/Optimization.php +24 -24
- package/src/php/Optimization/OptimizationOperators.php +26 -25
- 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 +38 -0
- 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/BlockList.php +99 -99
- 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 +129 -7
- package/src/php/Utils/TLSClientHelloParser.php +369 -369
- package/src/php/WordPress/WpDbStore.php +155 -0
- package/src/php/WordPress/fingerprint-wordpress.php +554 -0
- package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.mo +0 -0
- package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.po +262 -0
- package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.mo +0 -0
- package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.po +268 -0
- package/src/php/WordPress/package.php +218 -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,7 +1,7 @@
|
|
|
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';
|
|
@@ -14,7 +14,94 @@ function secureRandom() {
|
|
|
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,
|
|
@@ -168,12 +255,11 @@ async function solveSpaceChallenge(seed, queries, nonce, clientSecret, peerBlock
|
|
|
168
255
|
const finalBlock = new Uint8Array(finalCombined.length + nonceBytes.length);
|
|
169
256
|
finalBlock.set(finalCombined);
|
|
170
257
|
finalBlock.set(nonceBytes, finalCombined.length);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
return Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
258
|
+
|
|
259
|
+
return await safeDigestSha256Hex(finalBlock);
|
|
174
260
|
}
|
|
175
261
|
|
|
176
|
-
// ---
|
|
262
|
+
// --- Optimization functions adapted from library.js for client usage ---
|
|
177
263
|
|
|
178
264
|
function paretoDominates(objectivesA, objectivesB) {
|
|
179
265
|
let aIsBetterInOne = false;
|
|
@@ -736,11 +822,11 @@ async function solveOptimizationTask(initialPopulation, generations) {
|
|
|
736
822
|
}
|
|
737
823
|
|
|
738
824
|
/**
|
|
739
|
-
*
|
|
740
|
-
* @param {Uint8Array} baseBlock -
|
|
741
|
-
* @param {bigint} target -
|
|
742
|
-
* @param {Function} progressCallback -
|
|
743
|
-
* @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.
|
|
744
830
|
*/
|
|
745
831
|
async function solveCpuTargetInline(baseBlock, target, progressCallback) {
|
|
746
832
|
// --- FIX: Add validation for the target to prevent BigInt conversion errors ---
|
|
@@ -776,17 +862,18 @@ async function solveCpuTargetInline(baseBlock, target, progressCallback) {
|
|
|
776
862
|
if (typeof window !== 'undefined' && typeof Worker !== 'undefined') {
|
|
777
863
|
try {
|
|
778
864
|
const staticWorkerPath = window.ClientLibrary?.workerPath || (window.ClientConfig && window.ClientConfig.workerPath);
|
|
865
|
+
const sha256FnStr = sha256Sync.toString();
|
|
779
866
|
if (staticWorkerPath) {
|
|
780
867
|
return await new Promise((resolve, reject) => {
|
|
781
868
|
const worker = new Worker(staticWorkerPath);
|
|
782
869
|
worker.onmessage = (event) => {
|
|
783
|
-
if (event.data.type === 'progress') {
|
|
870
|
+
if (event.data && event.data.type === 'progress') {
|
|
784
871
|
if (progressCallback) progressCallback(event.data.solution);
|
|
785
|
-
} else if (event.data.type === 'success') {
|
|
872
|
+
} else if (event.data && event.data.type === 'success') {
|
|
786
873
|
resolve(event.data.solution);
|
|
787
874
|
worker.terminate();
|
|
788
|
-
} else if (event.data.solution !== undefined) {
|
|
789
|
-
// Fallback
|
|
875
|
+
} else if (event.data && event.data.solution !== undefined) {
|
|
876
|
+
// Fallback for backward compatibility with previous signature
|
|
790
877
|
resolve(event.data.solution);
|
|
791
878
|
worker.terminate();
|
|
792
879
|
}
|
|
@@ -804,15 +891,29 @@ async function solveCpuTargetInline(baseBlock, target, progressCallback) {
|
|
|
804
891
|
const { baseBlock, target } = e.data;
|
|
805
892
|
const cpuTarget = BigInt(target);
|
|
806
893
|
const encoder = new TextEncoder();
|
|
807
|
-
|
|
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;
|
|
808
897
|
while (true) {
|
|
809
898
|
const solutionBytes = encoder.encode(String(cpuSolution));
|
|
810
899
|
const finalBlock = new Uint8Array(baseBlock.length + solutionBytes.length);
|
|
811
900
|
finalBlock.set(baseBlock);
|
|
812
901
|
finalBlock.set(solutionBytes, baseBlock.length);
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
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;
|
|
816
917
|
cpuSolution++;
|
|
817
918
|
if (cpuSolution % 25000 === 0) {
|
|
818
919
|
self.postMessage({ type: 'progress', solution: cpuSolution });
|
|
@@ -847,24 +948,23 @@ async function solveCpuTargetInline(baseBlock, target, progressCallback) {
|
|
|
847
948
|
while (true) {
|
|
848
949
|
const solutionBytes = encoder.encode(String(cpuSolution));
|
|
849
950
|
|
|
850
|
-
//
|
|
951
|
+
// Direct binary concatenation: faster and safer.
|
|
851
952
|
const finalBlock = new Uint8Array(baseBlock.length + solutionBytes.length);
|
|
852
953
|
finalBlock.set(baseBlock);
|
|
853
954
|
finalBlock.set(solutionBytes, baseBlock.length);
|
|
854
955
|
|
|
855
956
|
if (cpuSolution === 0) {
|
|
856
|
-
//
|
|
957
|
+
// For debugging, print reconstructed message.
|
|
857
958
|
const reconstructedMsg = new TextDecoder().decode(finalBlock);
|
|
858
959
|
console.log(`[FP Client Solve] Hashing message: "${reconstructedMsg}"`);
|
|
859
960
|
}
|
|
860
961
|
|
|
861
|
-
const
|
|
862
|
-
|
|
863
|
-
// --- AJOUT DE LOGS POUR LE DÉBOGAGE CÔTÉ CLIENT ---
|
|
962
|
+
const hashHex = await safeDigestSha256Hex(finalBlock);
|
|
963
|
+
// --- CLIENT-SIDE DEBUG LOGS ---
|
|
864
964
|
if (cpuSolution === 0) { // Log only the first attempt
|
|
865
965
|
console.log(`[FP Client Solve] Attempt 0 hash: "0x${hashHex}"`);
|
|
866
966
|
}
|
|
867
|
-
// ---
|
|
967
|
+
// --- END DEBUG LOGS ---
|
|
868
968
|
if (BigInt('0x' + hashHex) < cpuTarget) break;
|
|
869
969
|
cpuSolution++;
|
|
870
970
|
if (cpuSolution % 50000 === 0) {
|
|
@@ -880,28 +980,28 @@ async function solveCpuTargetInline(baseBlock, target, progressCallback) {
|
|
|
880
980
|
}
|
|
881
981
|
|
|
882
982
|
/**
|
|
883
|
-
*
|
|
884
|
-
* @param {string} message -
|
|
885
|
-
* @param {bigint} target -
|
|
886
|
-
* @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.
|
|
887
987
|
*/
|
|
888
988
|
async function solveCpuTarget(message, target) {
|
|
889
989
|
// Vérifie si les Web Workers sont supportés par le navigateur.
|
|
890
990
|
if (typeof(Worker) === "undefined") {
|
|
891
991
|
console.warn("Web Workers not supported. Falling back to main thread calculation (UI may freeze).");
|
|
892
|
-
//
|
|
893
|
-
//
|
|
992
|
+
// An inline fallback implementation could be placed here if needed.
|
|
993
|
+
// For clarity, we assume workers are available.
|
|
894
994
|
throw new Error("Web Worker support is required for CPU challenges.");
|
|
895
995
|
}
|
|
896
996
|
|
|
897
997
|
return new Promise((resolve, reject) => {
|
|
898
|
-
//
|
|
899
|
-
//
|
|
998
|
+
// Create worker from dedicated script. The path must be publicly accessible.
|
|
999
|
+
// Ensure `pow.worker.js` is served by your static file server.
|
|
900
1000
|
const worker = new Worker('./pow.worker.js');
|
|
901
1001
|
|
|
902
1002
|
worker.onmessage = (event) => {
|
|
903
1003
|
resolve(event.data.solution);
|
|
904
|
-
worker.terminate(); //
|
|
1004
|
+
worker.terminate(); // Clean up worker once finished.
|
|
905
1005
|
};
|
|
906
1006
|
|
|
907
1007
|
worker.onerror = (error) => {
|
|
@@ -909,16 +1009,16 @@ async function solveCpuTarget(message, target) {
|
|
|
909
1009
|
worker.terminate();
|
|
910
1010
|
};
|
|
911
1011
|
|
|
912
|
-
//
|
|
1012
|
+
// Send challenge data to worker to begin computation.
|
|
913
1013
|
worker.postMessage({ message, target });
|
|
914
1014
|
});
|
|
915
1015
|
}
|
|
916
1016
|
|
|
917
1017
|
/**
|
|
918
|
-
*
|
|
919
|
-
* @param {string} seed -
|
|
920
|
-
* @param {number} difficulty -
|
|
921
|
-
* @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.
|
|
922
1022
|
*/
|
|
923
1023
|
async function solveMemory(seed, difficulty) {
|
|
924
1024
|
const wasmModule = typeof window !== 'undefined' ? (window.wasmModule || (window.ClientLibrary && window.ClientLibrary.wasmModule)) : null;
|
|
@@ -940,8 +1040,7 @@ async function solveMemory(seed, difficulty) {
|
|
|
940
1040
|
if (numBlocks === 0) return { solution: 0, merkleRoot: '', proofs: {} };
|
|
941
1041
|
|
|
942
1042
|
async function hashBlock(block) {
|
|
943
|
-
|
|
944
|
-
return Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
1043
|
+
return await safeDigestSha256Hex(block.buffer);
|
|
945
1044
|
}
|
|
946
1045
|
|
|
947
1046
|
function hexToBytes(hex) {
|
|
@@ -972,8 +1071,7 @@ async function solveMemory(seed, difficulty) {
|
|
|
972
1071
|
const left = currentLayer[i];
|
|
973
1072
|
const right = currentLayer[i + 1] || left;
|
|
974
1073
|
const combined = hexToBytes(left + right);
|
|
975
|
-
const
|
|
976
|
-
const hashHex = Array.from(new Uint8Array(hashBuf)).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
1074
|
+
const hashHex = await safeDigestSha256Hex(combined);
|
|
977
1075
|
nextLayer.push(hashHex);
|
|
978
1076
|
}
|
|
979
1077
|
tree.push(nextLayer);
|
|
@@ -1022,12 +1120,12 @@ async function solveMemory(seed, difficulty) {
|
|
|
1022
1120
|
}
|
|
1023
1121
|
|
|
1024
1122
|
/**
|
|
1025
|
-
*
|
|
1026
|
-
* NOTE:
|
|
1027
|
-
*
|
|
1028
|
-
* @param {Array<{x: number, y: number}>} cities -
|
|
1029
|
-
* @param {number} targetMaxDistance -
|
|
1030
|
-
* @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.
|
|
1031
1129
|
*/
|
|
1032
1130
|
async function solveTsp(cities, targetMaxDistance) {
|
|
1033
1131
|
// Utility function to calculate the distance between two cities
|
|
@@ -1086,9 +1184,9 @@ async function solveTsp(cities, targetMaxDistance) {
|
|
|
1086
1184
|
}
|
|
1087
1185
|
|
|
1088
1186
|
/**
|
|
1089
|
-
*
|
|
1090
|
-
* @param {object} challenge -
|
|
1091
|
-
* @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).
|
|
1092
1190
|
*/
|
|
1093
1191
|
async function solveChallenge(challenge, fingerprint = '') { // fingerprint parameter was already here, but unused in some calls
|
|
1094
1192
|
const { type, nonce, clientSecret, cpuTarget, memDifficulty, cities, clientIp, targetMaxDistance, queries, sizeMb } = challenge;
|
|
@@ -1114,7 +1212,7 @@ async function solveChallenge(challenge, fingerprint = '') { // fingerprint para
|
|
|
1114
1212
|
solutions.cpu = await solveCpuTargetInline(baseBlockBytes, target, null);
|
|
1115
1213
|
break;
|
|
1116
1214
|
case 'cpu_mem':
|
|
1117
|
-
//
|
|
1215
|
+
// For API calls, client IP is omitted from client-side message reconstruction
|
|
1118
1216
|
const memSeed = `:${nonce}:${clientSecret}`;
|
|
1119
1217
|
const [cpuSol, memSol] = await Promise.all([
|
|
1120
1218
|
(async () => {
|
|
@@ -1128,7 +1226,7 @@ async function solveChallenge(challenge, fingerprint = '') { // fingerprint para
|
|
|
1128
1226
|
solutions.mem = memSol;
|
|
1129
1227
|
break;
|
|
1130
1228
|
case 'cpu_mem_inline':
|
|
1131
|
-
//
|
|
1229
|
+
// Inline version for HTML compatibility with IP
|
|
1132
1230
|
const memSeedInline = `:${nonce}:${clientSecret}`;
|
|
1133
1231
|
const [cpuSolInline, memSolInline] = await Promise.all([
|
|
1134
1232
|
(async () => {
|
|
@@ -1157,8 +1255,8 @@ async function solveChallenge(challenge, fingerprint = '') { // fingerprint para
|
|
|
1157
1255
|
return solutions;
|
|
1158
1256
|
}
|
|
1159
1257
|
|
|
1160
|
-
// ---
|
|
1161
|
-
//
|
|
1258
|
+
// --- Direct HTML script injection compatibility ---
|
|
1259
|
+
// If loaded in browser context (window exists), expose methods on window.
|
|
1162
1260
|
if (typeof window !== 'undefined') {
|
|
1163
1261
|
window.solveCpuChallengeInline = solveCpuTargetInline;
|
|
1164
1262
|
window.solveMemoryChallenge = solveMemory;
|