aboutsummaryrefslogtreecommitdiff
path: root/web/js
diff options
context:
space:
mode:
Diffstat (limited to 'web/js')
-rw-r--r--web/js/main.mjs57
-rw-r--r--web/js/proof-of-work-slow.mjs18
-rw-r--r--web/js/proof-of-work.mjs32
3 files changed, 90 insertions, 17 deletions
diff --git a/web/js/main.mjs b/web/js/main.mjs
index daaafcf..01f21f0 100644
--- a/web/js/main.mjs
+++ b/web/js/main.mjs
@@ -37,7 +37,7 @@ const dependencies = [
const status = document.getElementById('status');
const image = document.getElementById('image');
const title = document.getElementById('title');
- const spinner = document.getElementById('spinner');
+ const progress = document.getElementById('progress');
const anubisVersion = JSON.parse(document.getElementById('anubis_version').textContent);
const ohNoes = ({
@@ -46,8 +46,7 @@ const dependencies = [
title.innerHTML = titleMsg;
status.innerHTML = statusMsg;
image.src = imageSrc;
- spinner.innerHTML = "";
- spinner.style.display = "none";
+ progress.style.display = "none";
};
if (!window.isSecureContext) {
@@ -68,8 +67,7 @@ const dependencies = [
// title.innerHTML = "Oh no!";
// status.innerHTML = "Checks failed. Please check your browser's settings and try again.";
// image.src = imageURL("sad");
- // spinner.innerHTML = "";
- // spinner.style.display = "none";
+ // progress.style.display = "none";
// return;
// }
@@ -112,20 +110,59 @@ const dependencies = [
return;
}
- status.innerHTML = `Calculating...<br/>Difficulty: ${rules.report_as}`;
- spinner.style.display = "block";
+ status.innerHTML = `Calculating...<br/>Difficulty: ${rules.report_as}, `;
+ progress.style.display = "inline-block";
+ // the whole text, including "Speed:", as a single node, because some browsers
+ // (Firefox mobile) present screen readers with each node as a separate piece
+ // of text.
+ const rateText = document.createTextNode("Speed: 0kH/s");
+ status.appendChild(rateText);
+
+ let lastSpeedUpdate = 0;
+ let showingApology = false;
+ const likelihood = Math.pow(16, -rules.report_as);
try {
const t0 = Date.now();
- const { hash, nonce } = await process(challenge, rules.difficulty);
+ const { hash, nonce } = await process(
+ challenge,
+ rules.difficulty,
+ (iters) => {
+ const delta = Date.now() - t0;
+ // only update the speed every second so it's less visually distracting
+ if (delta - lastSpeedUpdate > 1000) {
+ lastSpeedUpdate = delta;
+ rateText.data = `Speed: ${(iters / delta).toFixed(3)}kH/s`;
+ }
+
+ // the probability of still being on the page is (1 - likelihood) ^ iters.
+ // by definition, half of the time the progress bar only gets to half, so
+ // apply a polynomial ease-out function to move faster in the beginning
+ // and then slow down as things get increasingly unlikely. quadratic felt
+ // the best in testing, but this may need adjustment in the future.
+ const probability = Math.pow(1 - likelihood, iters);
+ const distance = (1 - Math.pow(probability, 2)) * 100;
+ progress["aria-valuenow"] = distance;
+ progress.firstElementChild.style.width = `${distance}%`;
+
+ if (probability < 0.1 && !showingApology) {
+ status.append(
+ document.createElement("br"),
+ document.createTextNode(
+ "Verification is taking longer than expected. Please do not refresh the page.",
+ ),
+ );
+ showingApology = true;
+ }
+ },
+ );
const t1 = Date.now();
console.log({ hash, nonce });
title.innerHTML = "Success!";
status.innerHTML = `Done! Took ${t1 - t0}ms, ${nonce} iterations`;
image.src = imageURL("happy", anubisVersion);
- spinner.innerHTML = "";
- spinner.style.display = "none";
+ progress.style.display = "none";
setTimeout(() => {
const redir = window.location.href;
diff --git a/web/js/proof-of-work-slow.mjs b/web/js/proof-of-work-slow.mjs
index e30dc21..6522c0b 100644
--- a/web/js/proof-of-work-slow.mjs
+++ b/web/js/proof-of-work-slow.mjs
@@ -1,6 +1,11 @@
// https://dev.to/ratmd/simple-proof-of-work-in-javascript-3kgm
-export default function process(data, difficulty = 5, _threads = 1) {
+export default function process(
+ data,
+ difficulty = 5,
+ progressCallback = null,
+ _threads = 1,
+) {
console.debug("slow algo");
return new Promise((resolve, reject) => {
let webWorkerURL = URL.createObjectURL(new Blob([
@@ -10,8 +15,12 @@ export default function process(data, difficulty = 5, _threads = 1) {
let worker = new Worker(webWorkerURL);
worker.onmessage = (event) => {
- worker.terminate();
- resolve(event.data);
+ if (typeof event.data === "number") {
+ progressCallback?.(event.data);
+ } else {
+ worker.terminate();
+ resolve(event.data);
+ }
};
worker.onerror = (event) => {
@@ -47,6 +56,9 @@ function processTask() {
let hash;
let nonce = 0;
do {
+ if (nonce & 1023 === 0) {
+ postMessage(nonce);
+ }
hash = await sha256(data + nonce++);
} while (hash.substring(0, difficulty) !== Array(difficulty + 1).join('0'));
diff --git a/web/js/proof-of-work.mjs b/web/js/proof-of-work.mjs
index b4f9c53..60d8d61 100644
--- a/web/js/proof-of-work.mjs
+++ b/web/js/proof-of-work.mjs
@@ -1,4 +1,9 @@
-export default function process(data, difficulty = 5, threads = (navigator.hardwareConcurrency || 1)) {
+export default function process(
+ data,
+ difficulty = 5,
+ progressCallback = null,
+ threads = (navigator.hardwareConcurrency || 1),
+) {
console.debug("fast algo");
return new Promise((resolve, reject) => {
let webWorkerURL = URL.createObjectURL(new Blob([
@@ -11,9 +16,12 @@ export default function process(data, difficulty = 5, threads = (navigator.hardw
let worker = new Worker(webWorkerURL);
worker.onmessage = (event) => {
- workers.forEach(worker => worker.terminate());
- worker.terminate();
- resolve(event.data);
+ if (typeof event.data === "number") {
+ progressCallback?.(event.data);
+ } else {
+ workers.forEach(worker => worker.terminate());
+ resolve(event.data);
+ }
};
worker.onerror = (event) => {
@@ -55,6 +63,8 @@ function processTask() {
let nonce = event.data.nonce;
let threads = event.data.threads;
+ const threadId = nonce;
+
while (true) {
const currentHash = await sha256(data + nonce);
const thisHash = new Uint8Array(currentHash);
@@ -78,7 +88,21 @@ function processTask() {
break;
}
+ const oldNonce = nonce;
nonce += threads;
+
+ // send a progess update every 1024 iterations. since each thread checks
+ // separate values, one simple way to do this is by bit masking the
+ // nonce for multiples of 1024. unfortunately, if the number of threads
+ // is not prime, only some of the threads will be sending the status
+ // update and they will get behind the others. this is slightly more
+ // complicated but ensures an even distribution between threads.
+ if (
+ nonce > oldNonce | 1023 && // we've wrapped past 1024
+ (nonce >> 10) % threads === threadId // and it's our turn
+ ) {
+ postMessage(nonce);
+ }
}
postMessage({