@anonympins/fingerprint 0.4.2 → 0.4.4

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.
@@ -1,198 +1,234 @@
1
- import {describe, expect, it, vi, beforeEach, afterEach} from 'vitest';
2
- import {createHash} from 'node:crypto';
3
- // Import the functions to be tested.
4
- // Since the inline file doesn't use exports, we can't directly import.
5
- // Instead, we'll test the ES module version which shares the same logic.
6
- import {solveChallenge, solveCpuTargetInline, solveMemory, solveTsp} from '../pow.solver.js';
7
-
8
- describe('Proof-of-Work Solvers', () => {
9
-
10
- describe('solveCpuTargetInline', () => {
11
- const nonce = 'test-nonce';
12
- // A relatively easy target for quick tests (first 16 bits must be zero)
13
- const targetHex = '0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
14
- const targetBigInt = BigInt('0x' + targetHex);
15
-
16
- it('should find a valid CPU solution with a simple base block', async () => {
17
- // Le `baseBlock` est maintenant un Uint8Array.
18
- // Le client et le serveur doivent s'accorder sur sa construction.
19
- // Ici, on simule un challenge simple avec juste le nonce.
20
- const baseBlock = new TextEncoder().encode(`${nonce}:`);
21
- const solution = await solveCpuTargetInline(baseBlock, targetHex);
22
-
23
- // Verification: re-hash the solution and check against the target
24
- const finalBlock = new Uint8Array([...baseBlock, ...new TextEncoder().encode(String(solution))]);
25
- const hash = createHash('sha256').update(finalBlock).digest('hex');
26
- const hashBigInt = BigInt('0x' + hash);
27
-
28
- expect(hashBigInt).toBeLessThan(targetBigInt);
29
- }, 20000);
30
-
31
- it('should find a valid CPU solution with a client secret', async () => {
32
- const clientSecret = 'my-secret';
33
- const fingerprint = 'test-fp-string';
34
- // Le `baseBlock` est maintenant un Uint8Array qui inclut toutes les informations.
35
- const baseBlock = new TextEncoder().encode(`${nonce}:${clientSecret}:${fingerprint}:`);
36
- const solution = await solveCpuTargetInline(baseBlock, targetHex);
37
-
38
- // Verification: re-hash the solution and check against the target
39
- const finalBlock = new Uint8Array([...baseBlock, ...new TextEncoder().encode(String(solution))]);
40
- const hash = createHash('sha256').update(finalBlock).digest('hex');
41
- const hashBigInt = BigInt('0x' + hash);
42
- expect(hashBigInt).toBeLessThan(targetBigInt);
43
- }, 20000);
44
-
45
- it('should call the progress callback', async () => {
46
- const progressCallback = vi.fn();
47
- const baseBlock = new TextEncoder().encode(`${nonce}:`);
48
- // Use a much harder target to ensure the loop runs long enough
49
- const hardTarget = '0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
50
- await solveCpuTargetInline(baseBlock, hardTarget, progressCallback);
51
-
52
- // Check if the callback was called with a number
53
- expect(progressCallback).toHaveBeenCalled();
54
- expect(progressCallback).toHaveBeenCalledWith(expect.any(Number));
55
- }, 40000); // Increase timeout for harder challenge
56
- });
57
-
58
- describe('solveMemory', () => {
59
- it('should produce a deterministic result for a given seed and difficulty', async () => {
60
- const seed = 'test-seed';
61
- const difficulty = 1; // 1MB
62
-
63
- const solution1 = await solveMemory(seed, difficulty);
64
- const solution2 = await solveMemory(seed, difficulty);
65
-
66
- expect(solution1).toBe(solution2);
67
- expect(solution1).not.toBe(await solveMemory('different-seed', difficulty));
68
- });
69
- });
70
-
71
- describe('solveTsp', () => {
72
- it('should solve a simple TSP problem', async () => {
73
- const cities = [{ x: 10, y: 10 }, { x: 90, y: 90 }, { x: 10, y: 90 }, { x: 90, y: 10 }];
74
- const result = await solveTsp(cities, 400);
75
-
76
- expect(result.path).toBeInstanceOf(Array);
77
- expect(result.path.length).toBe(4);
78
- // The optimal path for a square is ~324. The nearest neighbor should find this.
79
- expect(result.distance).toBeLessThan(330);
80
- });
81
- });
82
-
83
- describe('solveChallenge', () => {
84
- const nonce = 'challenge-nonce';
85
- const clientSecret = 'challenge-secret';
86
- const cpuTarget = '0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
87
-
88
- it('should solve a "cpu_target" challenge', async () => {
89
- // The 'cpu_target' case in solveChallenge uses Web Workers, which are not available in Node.js test env.
90
- // We will test the 'cpu_mem' case which uses the same underlying `solveCpuTargetInline` function.
91
- // This test is effectively covered by the 'cpu_mem' test below.
92
- expect(true).toBe(true);
93
- });
94
-
95
- it('should solve a "cpu_mem" challenge for an API client', async () => {
96
- const challenge = {
97
- type: 'cpu_mem',
98
- nonce,
99
- clientSecret,
100
- cpuTarget,
101
- memDifficulty: 1,
102
- // Pour les appels API, le serveur pré-calcule le baseBlock
103
- // sans l'IP du client, mais avec l'empreinte digitale.
104
- baseBlock: [...new TextEncoder().encode(`${nonce}:${clientSecret}:`)]
105
- };
106
-
107
- const solutions = await solveChallenge(challenge, ''); // Le fingerprint est passé en 2e arg
108
- expect(solutions.rawSolution).toHaveProperty('cpu', expect.any(Number));
109
- expect(solutions.rawSolution).toHaveProperty('mem', expect.any(Number));
110
- }, 20000);
111
-
112
- it('should solve a "cpu_mem_inline" challenge for a browser', async () => {
113
- const challenge = {
114
- type: 'cpu_mem_inline',
115
- nonce,
116
- clientSecret,
117
- cpuTarget,
118
- memDifficulty: 1,
119
- // Pour le challenge inline, le serveur inclut l'IP dans le baseBlock.
120
- baseBlock: [...new TextEncoder().encode(`${nonce}:${clientSecret}:`)]
121
- };
122
-
123
- const solutions = await solveChallenge(challenge, '');
124
- expect(solutions.rawSolution).toHaveProperty('cpu', expect.any(Number));
125
- expect(solutions.rawSolution).toHaveProperty('mem', expect.any(Number));
126
- }, 20000);
127
-
128
- it('should solve a "tsp" challenge', async () => {
129
- const challenge = {
130
- type: 'tsp',
131
- cities: [{ x: 0, y: 0 }, { x: 100, y: 100 }],
132
- targetMaxDistance: 300
133
- };
134
- const solutions = await solveChallenge(challenge);
135
- // Pour le TSP, la solution brute est directement le chemin
136
- expect(solutions.rawSolution).toEqual([0, 1]);
137
- });
138
-
139
- it('should throw an error for an unknown challenge type', async () => {
140
- const challenge = { type: 'unknown' };
141
- await expect(solveChallenge(challenge)).rejects.toThrow('Unknown challenge type: unknown');
142
- });
143
- });
144
-
145
- describe('WASM Solver Path Integration (Mocked)', () => {
146
- let mockWasmModule;
147
-
148
- beforeEach(() => {
149
- mockWasmModule = {
150
- _malloc: vi.fn().mockImplementation(() => 1234), // Simule un pointeur mémoire
151
- _free: vi.fn(),
152
- HEAPU8: new Uint8Array(10000),
153
- HEAP8: new Int8Array(10000),
154
- _solve_cpu_target: vi.fn().mockReturnValue(1337),
155
- _solve_memory_challenge: vi.fn().mockReturnValue(777)
156
- };
157
-
158
- // Injecter le module WASM globalement pour simuler le comportement du navigateur
159
- global.window = {
160
- wasmModule: mockWasmModule
161
- };
162
- });
163
-
164
- afterEach(() => {
165
- // Nettoyer l'environnement global après chaque test
166
- delete global.window;
167
- });
168
-
169
- it('should redirect solveCpuTargetInline to WASM and manage heap memory safely', async () => {
170
- const baseBlock = new Uint8Array([1, 2, 3, 4]);
171
- const target = '0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
172
-
173
- const solution = await solveCpuTargetInline(baseBlock, target);
174
-
175
- // 1. Vérification du résultat natif retourné par le mock
176
- expect(solution).toBe(1337);
177
-
178
- // 2. Vérification des allocations et libérations mémoire pour le bloc de base et la cible hexadécimale
179
- expect(mockWasmModule._malloc).toHaveBeenCalledTimes(2);
180
- expect(mockWasmModule._free).toHaveBeenCalledTimes(2);
181
-
182
- // 3. Vérification de l'appel à la fonction C++ exportée
183
- expect(mockWasmModule._solve_cpu_target).toHaveBeenCalledWith(1234, baseBlock.length, 1234);
184
- });
185
-
186
- it('should redirect solveMemory to WASM and manage heap memory safely', async () => {
187
- const seed = 'test-wasm-seed';
188
- const difficulty = 4; // 4 MB
189
-
190
- const solution = await solveMemory(seed, difficulty);
191
-
192
- expect(solution).toBe(777);
193
- expect(mockWasmModule._malloc).toHaveBeenCalledTimes(1);
194
- expect(mockWasmModule._free).toHaveBeenCalledTimes(1);
195
- expect(mockWasmModule._solve_memory_challenge).toHaveBeenCalledWith(1234, difficulty);
196
- });
197
- });
1
+ import {describe, expect, it, vi, beforeEach, afterEach} from 'vitest';
2
+ import {createHash} from 'node:crypto';
3
+ // Import the functions to be tested.
4
+ // Since the inline file doesn't use exports, we can't directly import.
5
+ // Instead, we'll test the ES module version which shares the same logic.
6
+ import {solveChallenge, solveCpuTargetInline, solveMemory, solveTsp} from '../pow.solver.js';
7
+
8
+ describe('Proof-of-Work Solvers', () => {
9
+
10
+ describe('solveCpuTargetInline', () => {
11
+ const nonce = 'test-nonce';
12
+ // A relatively easy target for quick tests (first 16 bits must be zero)
13
+ const targetHex = '0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
14
+ const targetBigInt = BigInt('0x' + targetHex);
15
+
16
+ it('should find a valid CPU solution with a simple base block', async () => {
17
+ // Le `baseBlock` est maintenant un Uint8Array.
18
+ // Le client et le serveur doivent s'accorder sur sa construction.
19
+ // Ici, on simule un challenge simple avec juste le nonce.
20
+ const baseBlock = new TextEncoder().encode(`${nonce}:`);
21
+ const solution = await solveCpuTargetInline(baseBlock, targetHex);
22
+
23
+ // Verification: re-hash the solution and check against the target
24
+ const finalBlock = new Uint8Array([...baseBlock, ...new TextEncoder().encode(String(solution))]);
25
+ const hash = createHash('sha256').update(finalBlock).digest('hex');
26
+ const hashBigInt = BigInt('0x' + hash);
27
+
28
+ expect(hashBigInt).toBeLessThan(targetBigInt);
29
+ }, 20000);
30
+
31
+ it('should find a valid CPU solution with a client secret', async () => {
32
+ const clientSecret = 'my-secret';
33
+ const fingerprint = 'test-fp-string';
34
+ // Le `baseBlock` est maintenant un Uint8Array qui inclut toutes les informations.
35
+ const baseBlock = new TextEncoder().encode(`${nonce}:${clientSecret}:${fingerprint}:`);
36
+ const solution = await solveCpuTargetInline(baseBlock, targetHex);
37
+
38
+ // Verification: re-hash the solution and check against the target
39
+ const finalBlock = new Uint8Array([...baseBlock, ...new TextEncoder().encode(String(solution))]);
40
+ const hash = createHash('sha256').update(finalBlock).digest('hex');
41
+ const hashBigInt = BigInt('0x' + hash);
42
+ expect(hashBigInt).toBeLessThan(targetBigInt);
43
+ }, 20000);
44
+
45
+ it('should call the progress callback', async () => {
46
+ const progressCallback = vi.fn();
47
+ const baseBlock = new TextEncoder().encode(`${nonce}:`);
48
+ // Use a much harder target to ensure the loop runs long enough
49
+ const hardTarget = '0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
50
+ await solveCpuTargetInline(baseBlock, hardTarget, progressCallback);
51
+
52
+ // Check if the callback was called with a number
53
+ expect(progressCallback).toHaveBeenCalled();
54
+ expect(progressCallback).toHaveBeenCalledWith(expect.any(Number));
55
+ }, 40000); // Increase timeout for harder challenge
56
+ });
57
+
58
+ describe('solveMemory', () => {
59
+ it('should produce a deterministic result for a given seed and difficulty', async () => {
60
+ const seed = 'test-seed';
61
+ const difficulty = 1; // 1MB
62
+
63
+ const solution1 = await solveMemory(seed, difficulty);
64
+ const solution2 = await solveMemory(seed, difficulty);
65
+
66
+ expect(solution1).toBe(solution2);
67
+ expect(solution1).not.toBe(await solveMemory('different-seed', difficulty));
68
+ });
69
+ });
70
+
71
+ describe('solveTsp', () => {
72
+ it('should solve a simple TSP problem', async () => {
73
+ const cities = [{ x: 10, y: 10 }, { x: 90, y: 90 }, { x: 10, y: 90 }, { x: 90, y: 10 }];
74
+ const result = await solveTsp(cities, 400);
75
+
76
+ expect(result.path).toBeInstanceOf(Array);
77
+ expect(result.path.length).toBe(4);
78
+ // The optimal path for a square is ~324. The nearest neighbor should find this.
79
+ expect(result.distance).toBeLessThan(330);
80
+ });
81
+ });
82
+
83
+ describe('solveChallenge', () => {
84
+ const nonce = 'challenge-nonce';
85
+ const clientSecret = 'challenge-secret';
86
+ const cpuTarget = '0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
87
+
88
+ it('should solve a "cpu_target" challenge', async () => {
89
+ // The 'cpu_target' case in solveChallenge uses Web Workers, which are not available in Node.js test env.
90
+ // We will test the 'cpu_mem' case which uses the same underlying `solveCpuTargetInline` function.
91
+ // This test is effectively covered by the 'cpu_mem' test below.
92
+ expect(true).toBe(true);
93
+ });
94
+
95
+ it('should solve a "cpu_mem" challenge for an API client', async () => {
96
+ const challenge = {
97
+ type: 'cpu_mem',
98
+ nonce,
99
+ clientSecret,
100
+ cpuTarget,
101
+ memDifficulty: 1,
102
+ // Pour les appels API, le serveur pré-calcule le baseBlock
103
+ // sans l'IP du client, mais avec l'empreinte digitale.
104
+ baseBlock: [...new TextEncoder().encode(`${nonce}:${clientSecret}:`)]
105
+ };
106
+
107
+ const solutions = await solveChallenge(challenge, ''); // Le fingerprint est passé en 2e arg
108
+ expect(solutions.rawSolution).toHaveProperty('cpu', expect.any(Number));
109
+ expect(solutions.rawSolution).toHaveProperty('mem', expect.any(Number));
110
+ }, 20000);
111
+
112
+ it('should solve a "cpu_mem_inline" challenge for a browser', async () => {
113
+ const challenge = {
114
+ type: 'cpu_mem_inline',
115
+ nonce,
116
+ clientSecret,
117
+ cpuTarget,
118
+ memDifficulty: 1,
119
+ // Pour le challenge inline, le serveur inclut l'IP dans le baseBlock.
120
+ baseBlock: [...new TextEncoder().encode(`${nonce}:${clientSecret}:`)]
121
+ };
122
+
123
+ const solutions = await solveChallenge(challenge, '');
124
+ expect(solutions.rawSolution).toHaveProperty('cpu', expect.any(Number));
125
+ expect(solutions.rawSolution).toHaveProperty('mem', expect.any(Number));
126
+ }, 20000);
127
+
128
+ it('should solve a "tsp" challenge', async () => {
129
+ const challenge = {
130
+ type: 'tsp',
131
+ cities: [{ x: 0, y: 0 }, { x: 100, y: 100 }],
132
+ targetMaxDistance: 300
133
+ };
134
+ const solutions = await solveChallenge(challenge);
135
+ // Pour le TSP, la solution brute est directement le chemin
136
+ expect(solutions.rawSolution).toEqual([0, 1]);
137
+ });
138
+
139
+ it('should solve a "useful_work_task" with facility location challenge', async () => {
140
+ const challenge = {
141
+ type: 'useful_work_task',
142
+ nonce: 'test-nonce-useful-work',
143
+ usefulWorkTask: {
144
+ problemId: 'facility_location_challenge',
145
+ task: {
146
+ type: 'simulated_annealing_iterations',
147
+ iterations: 10,
148
+ payload: {
149
+ customers: [
150
+ { x: 100, y: 100 },
151
+ { x: 200, y: 200 }
152
+ ],
153
+ numFacilities: 2,
154
+ bounds: { minX: 0, maxX: 500, minY: 0, maxY: 500 },
155
+ options: {
156
+ fixedCostPerFacility: 1500,
157
+ initialTemperature: 150000,
158
+ coolingRate: 0.99
159
+ }
160
+ }
161
+ }
162
+ }
163
+ };
164
+
165
+ const solutions = await solveChallenge(challenge);
166
+ expect(solutions.type).toBe('useful_work_task');
167
+ expect(solutions.nonce).toBe('test-nonce-useful-work');
168
+ expect(solutions.rawSolution.problem_id).toBe('facility_location_challenge');
169
+ expect(solutions.rawSolution.work_result).toBeDefined();
170
+ expect(solutions.rawSolution.work_result.solution).toBeInstanceOf(Array);
171
+ expect(solutions.rawSolution.work_result.solution.length).toBe(2);
172
+ expect(solutions.rawSolution.work_result.energy).toBeLessThan(Infinity);
173
+ });
174
+
175
+ it('should throw an error for an unknown challenge type', async () => {
176
+ const challenge = { type: 'unknown' };
177
+ await expect(solveChallenge(challenge)).rejects.toThrow('Unknown challenge type: unknown');
178
+ });
179
+ });
180
+
181
+ describe('WASM Solver Path Integration (Mocked)', () => {
182
+ let mockWasmModule;
183
+
184
+ beforeEach(() => {
185
+ mockWasmModule = {
186
+ _malloc: vi.fn().mockImplementation(() => 1234), // Simule un pointeur mémoire
187
+ _free: vi.fn(),
188
+ HEAPU8: new Uint8Array(10000),
189
+ HEAP8: new Int8Array(10000),
190
+ _solve_cpu_target: vi.fn().mockReturnValue(1337),
191
+ _solve_memory_challenge: vi.fn().mockReturnValue(777)
192
+ };
193
+
194
+ // Injecter le module WASM globalement pour simuler le comportement du navigateur
195
+ global.window = {
196
+ wasmModule: mockWasmModule
197
+ };
198
+ });
199
+
200
+ afterEach(() => {
201
+ // Nettoyer l'environnement global après chaque test
202
+ delete global.window;
203
+ });
204
+
205
+ it('should redirect solveCpuTargetInline to WASM and manage heap memory safely', async () => {
206
+ const baseBlock = new Uint8Array([1, 2, 3, 4]);
207
+ const target = '0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
208
+
209
+ const solution = await solveCpuTargetInline(baseBlock, target);
210
+
211
+ // 1. Vérification du résultat natif retourné par le mock
212
+ expect(solution).toBe(1337);
213
+
214
+ // 2. Vérification des allocations et libérations mémoire pour le bloc de base et la cible hexadécimale
215
+ expect(mockWasmModule._malloc).toHaveBeenCalledTimes(2);
216
+ expect(mockWasmModule._free).toHaveBeenCalledTimes(2);
217
+
218
+ // 3. Vérification de l'appel à la fonction C++ exportée
219
+ expect(mockWasmModule._solve_cpu_target).toHaveBeenCalledWith(1234, baseBlock.length, 1234);
220
+ });
221
+
222
+ it('should redirect solveMemory to WASM and manage heap memory safely', async () => {
223
+ const seed = 'test-wasm-seed';
224
+ const difficulty = 4; // 4 MB
225
+
226
+ const solution = await solveMemory(seed, difficulty);
227
+
228
+ expect(solution).toBe(777);
229
+ expect(mockWasmModule._malloc).toHaveBeenCalledTimes(1);
230
+ expect(mockWasmModule._free).toHaveBeenCalledTimes(1);
231
+ expect(mockWasmModule._solve_memory_challenge).toHaveBeenCalledWith(1234, difficulty);
232
+ });
233
+ });
198
234
  });