@elsikora/x-captcha-react
Version:
React components for X-Captcha service
123 lines (109 loc) • 4.7 kB
JavaScript
'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