Weighs the soul of incoming HTTP requests to stop AI crawlers
at main 152 lines 4.5 kB view raw
1import processFast from "./proof-of-work.mjs"; 2import processSlow from "./proof-of-work-slow.mjs"; 3 4const defaultDifficulty = 4; 5const algorithms = { 6 fast: processFast, 7 slow: processSlow, 8}; 9 10const status = document.getElementById("status"); 11const difficultyInput = document.getElementById("difficulty-input"); 12const algorithmSelect = document.getElementById("algorithm-select"); 13const compareSelect = document.getElementById("compare-select"); 14const header = document.getElementById("table-header"); 15const headerCompare = document.getElementById("table-header-compare"); 16const results = document.getElementById("results"); 17 18const setupControls = () => { 19 difficultyInput.value = defaultDifficulty; 20 for (const alg of Object.keys(algorithms)) { 21 const option1 = document.createElement("option"); 22 algorithmSelect.append(option1); 23 const option2 = document.createElement("option"); 24 compareSelect.append(option2); 25 option1.value = option1.innerText = option2.value = option2.innerText = alg; 26 } 27}; 28 29const benchmarkTrial = async (stats, difficulty, algorithm, signal) => { 30 if (!(difficulty >= 1)) { 31 throw new Error(`Invalid difficulty: ${difficulty}`); 32 } 33 const process = algorithms[algorithm]; 34 if (process == null) { 35 throw new Error(`Unknown algorithm: ${algorithm}`); 36 } 37 38 const rawChallenge = new Uint8Array(32); 39 crypto.getRandomValues(rawChallenge); 40 const challenge = Array.from(rawChallenge) 41 .map((c) => c.toString(16).padStart(2, "0")) 42 .join(""); 43 44 const t0 = performance.now(); 45 const { hash, nonce } = await process(challenge, Number(difficulty), signal); 46 const t1 = performance.now(); 47 console.log({ hash, nonce }); 48 49 stats.time += t1 - t0; 50 stats.iters += nonce; 51 52 return { time: t1 - t0, nonce }; 53}; 54 55const stats = { time: 0, iters: 0 }; 56const comparison = { time: 0, iters: 0 }; 57const updateStatus = () => { 58 const mainRate = stats.iters / stats.time; 59 const compareRate = comparison.iters / comparison.time; 60 if (Number.isFinite(mainRate)) { 61 status.innerText = `Average hashrate: ${mainRate.toFixed(3)}kH/s`; 62 if (Number.isFinite(compareRate)) { 63 const change = ((mainRate - compareRate) / mainRate) * 100; 64 status.innerText += ` vs ${compareRate.toFixed(3)}kH/s (${change.toFixed(2)}% change)`; 65 } 66 } else { 67 status.innerText = "Benchmarking..."; 68 } 69}; 70 71const tableCell = (text) => { 72 const td = document.createElement("td"); 73 td.innerText = text; 74 td.style.padding = "0 0.25rem"; 75 return td; 76}; 77 78const benchmarkLoop = async (controller) => { 79 const difficulty = difficultyInput.value; 80 const algorithm = algorithmSelect.value; 81 const compareAlgorithm = compareSelect.value; 82 updateStatus(); 83 84 try { 85 const { time, nonce } = await benchmarkTrial( 86 stats, 87 difficulty, 88 algorithm, 89 controller.signal, 90 ); 91 92 const tr = document.createElement("tr"); 93 tr.style.display = "contents"; 94 tr.append(tableCell(`${time}ms`), tableCell(nonce)); 95 96 // auto-scroll to new rows 97 const atBottom = 98 results.scrollHeight - results.clientHeight <= results.scrollTop; 99 results.append(tr); 100 if (atBottom) { 101 results.scrollTop = results.scrollHeight - results.clientHeight; 102 } 103 updateStatus(); 104 105 if (compareAlgorithm !== "NONE") { 106 const { time, nonce } = await benchmarkTrial( 107 comparison, 108 difficulty, 109 compareAlgorithm, 110 controller.signal, 111 ); 112 tr.append(tableCell(`${time}ms`), tableCell(nonce)); 113 } 114 } catch (e) { 115 if (e !== false) { 116 status.innerText = e; 117 } 118 return; 119 } 120 121 await benchmarkLoop(controller); 122}; 123 124let controller = null; 125const reset = () => { 126 stats.time = stats.iters = 0; 127 comparison.time = comparison.iters = 0; 128 results.innerHTML = status.innerText = ""; 129 130 const table = results.parentElement; 131 if (compareSelect.value !== "NONE") { 132 table.style.gridTemplateColumns = "repeat(4,auto)"; 133 header.style.display = "none"; 134 headerCompare.style.display = "contents"; 135 } else { 136 table.style.gridTemplateColumns = "repeat(2,auto)"; 137 header.style.display = "contents"; 138 headerCompare.style.display = "none"; 139 } 140 141 if (controller != null) { 142 controller.abort(); 143 } 144 controller = new AbortController(); 145 void benchmarkLoop(controller); 146}; 147 148setupControls(); 149difficultyInput.addEventListener("change", reset); 150algorithmSelect.addEventListener("change", reset); 151compareSelect.addEventListener("change", reset); 152reset();