// Web Worker Script for hash computation function workerFunction() { self.onmessage = function(e) { const { type, data } = e.data; if (type === 'pow') { // PoW calculation const { challenge, salt, startNonce, endNonce, target, batchId } = data; let count = 0; let solution = null; processNextNonce(startNonce); function processNextNonce(nonce) { const input = String(challenge) + String(salt) + nonce.toString(); const msgBuffer = new TextEncoder().encode(input); crypto.subtle.digest('SHA-256', msgBuffer) .then(hashBuffer => { const hashArray = Array.from(new Uint8Array(hashBuffer)); const result = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); count++; if (result.startsWith(target)) { solution = { nonce: nonce.toString(), found: true }; self.postMessage({ type: 'pow_result', solution: solution, count: count, batchId: batchId }); return; } if (count % 1000 === 0) { self.postMessage({ type: 'progress', count: count, batchId: batchId }); } if (nonce < endNonce && !solution) { setTimeout(() => processNextNonce(nonce + 1), 0); } else if (!solution) { self.postMessage({ type: 'pow_result', solution: null, count: count, batchId: batchId }); } }) .catch(err => { self.postMessage({ type: 'error', error: 'Crypto API error: ' + err.message }); }); } } else { // Handle other message types if needed in the future self.postMessage({ type: 'error', error: 'Unknown message type: ' + type }); } }; } const workerCode = "(" + workerFunction.toString() + ")()"; // Proof-of-Space Worker script with buffer pooling function posWorkerFunction() { self.onmessage = async function(e) { const { type, seedHex, isDecoy } = e.data; if (type === 'pos') { const minMB = 48, maxMB = 160; let seedInt = parseInt(seedHex.slice(0, 8), 16); if (isNaN(seedInt)) seedInt = Math.floor(Math.random() * (maxMB - minMB + 1)); const CHUNK_MB = isDecoy ? (minMB + ((seedInt * 3 + 17) % (maxMB - minMB + 1))) : (minMB + (seedInt % (maxMB - minMB + 1))); const CHUNK_SIZE = CHUNK_MB * 1024 * 1024; const chunkCount = 4 + (seedInt % 5); const chunkSize = Math.floor(CHUNK_SIZE / chunkCount); const FILL_STEP_4K = 4096, FILL_STEP_1K = 1024; const FILL_STEP_SWITCH = 35 * 1024 * 1024; const runs = 3; // Pre-allocate buffers // Removed baseBuf as cpuBase calculation is unused const mainBuf = new ArrayBuffer(CHUNK_SIZE); const view = new Uint8Array(mainBuf); const pressureBuf = new ArrayBuffer(16 * 1024 * 1024); const pressureView = new Uint8Array(pressureBuf); // Removed CPU baseline calculation as it's unused upstream const hashes = []; const times = []; for (let r = 0; r < runs; r++) { const prng = seededPRNG(seedHex + r.toString(16)); // generate deterministic chunk order const order = Array.from({ length: chunkCount }, (_, i) => i); for (let i = order.length - 1; i > 0; i--) { const j = prng() % (i + 1); [order[i], order[j]] = [order[j], order[i]]; } // fill view const t0 = performance.now(); for (let c = 0; c < chunkCount; c++) { const idx = order[c]; const start = idx * chunkSize; const end = (idx === chunkCount - 1) ? CHUNK_SIZE : start + chunkSize; const step = (start < FILL_STEP_SWITCH) ? FILL_STEP_4K : FILL_STEP_1K; for (let i = start; i < end; i += step) view[i] = prng() & 0xFF; } const hashBuf = await crypto.subtle.digest('SHA-256', view); const t2 = performance.now(); hashes.push(Array.from(new Uint8Array(hashBuf)).map(b => b.toString(16).padStart(2, '0')).join('')); times.push(Math.round(t2 - t0)); // memory pressure for (let i = 0; i < pressureView.length; i += 4096) pressureView[i] = prng() & 0xFF; } // Removed cpuBase from postMessage self.postMessage({ type: 'pos_result', hashes, times }); } }; function seededPRNG(seedHex) { const s = []; for (let i = 0; i < 4; i++) s[i] = parseInt(seedHex.substr(i * 8, 8), 16) >>> 0; function rotl(x, k) { return ((x << k) | (x >>> (32 - k))) >>> 0; } return function() { const t = s[1] << 9; let r = (s[0] * 5) >>> 0; r = rotl(r, 7) * 9 >>> 0; const tmp = s[0] ^ s[2]; s[2] ^= s[1]; s[1] ^= s[3]; s[0] ^= s[1]; s[3] ^= tmp; s[2] ^= t; s[3] = rotl(s[3], 11); return r >>> 0; }; } } const posWorkerCode = "(" + posWorkerFunction.toString() + ")()"; // Main verification script document.addEventListener('DOMContentLoaded', function() { setTimeout(initVerification, 650); function initVerification() { const dataEl = document.getElementById('verification-data'); const targetPath = dataEl.getAttribute('data-target'); const requestID = dataEl.getAttribute('data-request-id'); startVerification(); async function startVerification() { try { const challengeResponse = await fetch('/api/pow/challenge?id=' + encodeURIComponent(requestID), { method: 'GET', headers: { 'Accept': 'application/json' } }); if (!challengeResponse.ok) { throw new Error('Failed to get challenge parameters'); } const challengeData = await challengeResponse.json(); // Extract real and decoy seeds using obfuscated keys const realPosSeed = challengeData.d; // 'd' is pos_seed const decoySeed = challengeData.e || (Math.random().toString(16).slice(2, 18)); // 'e' is decoy_seed const decoyFields = challengeData.f || []; // 'f' is decoy_fields const verifier = new Verifier(challengeData, targetPath, requestID, decoySeed, decoyFields); verifier.start(); } catch (error) { showError('Verification setup failed: ' + error.message); } } function createWorker() { const blob = new Blob([workerCode], { type: 'text/javascript' }); return new Worker(URL.createObjectURL(blob)); } function createPosWorker() { const blob = new Blob([posWorkerCode], { type: 'text/javascript' }); return new Worker(URL.createObjectURL(blob)); } function showError(message) { const container = document.querySelector('.container'); container.classList.add('error'); container.classList.remove('success'); // Let CSS pseudo-elements render the ring and X on the existing spinner const spinnerEl = document.querySelector('.spinner'); const statusEl = document.getElementById('status'); statusEl.style.display = 'inline-block'; statusEl.textContent = ''; // Keep this behavior as original, even if odd statusEl.classList.add('error'); statusEl.classList.remove('success'); const spinnerContainer = document.querySelector('.spinner-container'); let errorDetails = document.getElementById('error-details'); if (!errorDetails) { errorDetails = document.createElement('div'); errorDetails.id = 'error-details'; errorDetails.className = 'error-details'; spinnerContainer.appendChild(errorDetails); } // Hide error details to match success state layout errorDetails.style.display = 'none'; // Keep this behavior as original } function showSuccess() { document.querySelector('.container').classList.add('success'); document.getElementById('status').textContent = 'Redirecting'; } function Verifier(params, targetPath, requestID, decoySeed, decoyFields) { const workers = []; const activeBatches = {}; let powSolution = null; let isRunning = false; const cpuCount = navigator.hardwareConcurrency || 4; const workerCount = Math.max(1, Math.floor(cpuCount * 0.8)); const REDIRECT_DELAY = 1488; this.start = function() { setTimeout(findProofOfWork, 100); }; async function findProofOfWork() { try { isRunning = true; let decodedChallenge, decodedSalt; try { decodedChallenge = atob(params.a); decodedSalt = atob(params.b); } catch (e) { throw new Error(`Failed to decode challenge/salt: ${e.message}`); } const target = '0'.repeat(params.c); for (let i = 0; i < workerCount; i++) { const worker = createWorker(); // Pass only 'e.data' as workerId parameter was unused worker.onmessage = e => handleWorkerMessage(e.data); worker.onerror = error => { // Silently handle worker errors }; workers.push(worker); } const totalRange = Number.MAX_SAFE_INTEGER; const rangePerWorker = Math.floor(totalRange / workerCount); for (let i = 0; i < workers.length; i++) { const startNonce = i * rangePerWorker; const endNonce = (i === workers.length - 1) ? totalRange : (i + 1) * rangePerWorker - 1; const workerId = `pow-worker-${i}`; activeBatches[workerId] = { // Keep workerId here as it's used as key in activeBatches workerId: i, startNonce, endNonce }; workers[i].postMessage({ type: 'pow', data: { challenge: decodedChallenge, salt: decodedSalt, startNonce, endNonce, target, batchId: workerId } }); } } catch (error) { terminateWorkers(); showError(error.message); } } // Removed unused 'workerId' parameter function handleWorkerMessage(data) { if (!isRunning) return; if (data.type === 'pow_result') { if (activeBatches[data.batchId]) { delete activeBatches[data.batchId]; if (data.solution && data.solution.found) { if (!powSolution) { powSolution = data.solution; proofOfWorkFound(powSolution); } } } } else if (data.type === 'error') { showError('Compatibility error: ' + data.error); terminateWorkers(); } } async function proofOfWorkFound(solution) { isRunning = false; terminateWorkers(); try { // PoS via Worker const posResult = await new Promise(res => { const w = createPosWorker(); w.onmessage = e => { if (e.data.type==='pos_result') { res(e.data); w.terminate(); }}; w.postMessage({ type:'pos', seedHex: params.d, isDecoy:false }); }); const decoyResult = await new Promise(res => { const w = createPosWorker(); w.onmessage = e => { if (e.data.type==='pos_result') { res(e.data); w.terminate(); }}; w.postMessage({ type:'pos', seedHex: decoySeed, isDecoy:true }); }); // Submit results await submitSolution({ requestID, g: solution.nonce, h: posResult.hashes, i: posResult.times, j: decoyResult.hashes, k: decoyResult.times, l: decoyFields }); } catch (error) { showError(error.message); } } function terminateWorkers() { workers.forEach(worker => worker.terminate()); } async function submitSolution(solutionData) { try { const response = await fetch('/api/pow/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ request_id: solutionData.requestID, // Keep descriptive g: solutionData.g, // Nonce h: solutionData.h, // Real PoS Hashes i: solutionData.i, // Real PoS Times j: solutionData.j, // Decoy Hashes k: solutionData.k, // Decoy Times l: solutionData.l // Decoy Fields }) }); if (!response.ok) { let errorMsg = `Verification failed: ${response.statusText}`; try { const errorData = await response.json(); if (errorData && errorData.error) { errorMsg += ` - ${errorData.error}`; } else { const text = await response.text(); errorMsg += ` - Response: ${text}`; } } catch (parseError) { // Silent catch } showError(errorMsg); return; } showSuccess(); setTimeout(() => { window.location.href = targetPath; }, REDIRECT_DELAY); } catch (error) { showError('Verification failed. Please refresh the page.'); } } } } });