UNPKG

@elsikora/x-captcha-react

Version:

React components for X-Captcha service

123 lines (109 loc) 4.7 kB
'use strict'; var poweSolver_constant = require('../constant/powe-solver.constant.js'); var powSolverWorkerStatus_enum = require('../enum/pow-solver-worker-status.enum.js'); /** * Class for solving Proof of Work challenges using Web Workers */ const PowSolver = { /** * Solve a PoW challenge by finding a nonce that produces a hash with the required number of leading zeros * @param {IPowSolverChallenge} challengeData - The challenge data containing prefix and difficulty * @param {IPowSolverConfig} [config] - Configuration options for the solver * @returns {Promise<IPowSolverSolution>} The solution (nonce and hash) that satisfies the challenge */ async solve(challengeData, config) { // Create a web worker from blob URL for better compatibility const workerCode = ` // SHA-256 implementation for the worker async function sha256(message) { // Encode as UTF-8 const messageBuffer = new TextEncoder().encode(message); // Hash the message using Web Crypto API const hashBuffer = await crypto.subtle.digest("SHA-256", messageBuffer); // Convert ArrayBuffer to Array const hashArray = Array.from(new Uint8Array(hashBuffer)); // Convert bytes to hex string return hashArray.map(b => b.toString(16).padStart(2, "0")).join(""); } // Listen for messages from the main thread self.addEventListener("message", async (e) => { const { difficulty, prefix, maxAttempts, batchSize } = e.data; const target = "0".repeat(difficulty); let nonce = 0; let hash = ""; let found = false; // Process in batches to allow progress reporting for (let attempt = 0; attempt < maxAttempts; attempt++) { const input = \`\${prefix}\${nonce}\`; hash = await sha256(input); if (hash.startsWith(target)) { found = true; self.postMessage({ status: "success", solution: { nonce: nonce.toString(), hash }, attempt }); break; } nonce++; // Report progress periodically if (attempt % batchSize === 0) { self.postMessage({ status: "progress", progress: (attempt / maxAttempts) * 100, attempt }); } } if (!found) { self.postMessage({ status: "failed", error: \`Failed to solve PoW challenge after \${maxAttempts} attempts\` }); } }); `; // eslint-disable-next-line @elsikora/node/no-unsupported-features/node-builtins const blob = new Blob([workerCode], { type: "application/javascript" }); // eslint-disable-next-line @elsikora/node/no-unsupported-features/node-builtins const workerUrl = URL.createObjectURL(blob); const worker = new Worker(workerUrl); return new Promise((resolve, reject) => { const timeout = setTimeout(() => { worker.terminate(); // eslint-disable-next-line @elsikora/node/no-unsupported-features/node-builtins URL.revokeObjectURL(workerUrl); reject(new Error("PoW solver timed out")); }, config?.workerTimeout ?? poweSolver_constant.default.WORKER_TIMEOUT); worker.addEventListener("message", (event) => { const { error, solution, status } = event.data; if (status === powSolverWorkerStatus_enum.EPowSolverWorkerStatus.SUCCESS) { // eslint-disable-next-line @elsikora/typescript/no-unsafe-argument clearTimeout(timeout); worker.terminate(); // eslint-disable-next-line @elsikora/node/no-unsupported-features/node-builtins URL.revokeObjectURL(workerUrl); resolve(solution); } else if (status === powSolverWorkerStatus_enum.EPowSolverWorkerStatus.FAILED) { // eslint-disable-next-line @elsikora/typescript/no-unsafe-argument clearTimeout(timeout); worker.terminate(); // eslint-disable-next-line @elsikora/node/no-unsupported-features/node-builtins URL.revokeObjectURL(workerUrl); reject(new Error(error)); } }); worker.postMessage({ ...challengeData, batchSize: config?.batchSize ?? poweSolver_constant.default.BATCH_SIZE, maxAttempts: config?.maxAttempts ?? poweSolver_constant.default.MAX_ATTEMPTS, }); }); }, }; exports.PowSolver = PowSolver; //# sourceMappingURL=pow-solver.utility.js.map