aboutsummaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
authorXe Iaso <me@xeiaso.net>2025-04-09 00:12:38 -0400
committerXe Iaso <me@xeiaso.net>2025-04-09 00:12:38 -0400
commitcc1d5b71da0cbc79c3f84a2f59d3daa38d1a06e6 (patch)
tree7d6ce1d0d950809a00caa50af60ac146afffb952 /web
parent2324395ae2dcdb4729f9c29e393a6a03a6e84af6 (diff)
downloadanubis-cc1d5b71da0cbc79c3f84a2f59d3daa38d1a06e6.tar.xz
anubis-cc1d5b71da0cbc79c3f84a2f59d3daa38d1a06e6.zip
experiment: start implementing checks in wasm (client side only so far)
Signed-off-by: Xe Iaso <me@xeiaso.net>
Diffstat (limited to 'web')
-rw-r--r--web/js/algos/fast.mjs (renamed from web/js/proof-of-work.mjs)2
-rw-r--r--web/js/algos/sha256.mjs160
-rw-r--r--web/js/algos/slow.mjs (renamed from web/js/proof-of-work-slow.mjs)1
-rw-r--r--web/js/bench.mjs17
-rw-r--r--web/js/main.mjs23
-rw-r--r--web/js/xeact.mjs13
-rw-r--r--web/static/wasm/.gitignore2
7 files changed, 198 insertions, 20 deletions
diff --git a/web/js/proof-of-work.mjs b/web/js/algos/fast.mjs
index 5ef3a8a..a077c22 100644
--- a/web/js/proof-of-work.mjs
+++ b/web/js/algos/fast.mjs
@@ -5,7 +5,6 @@ export default function process(
progressCallback = null,
threads = (navigator.hardwareConcurrency || 1),
) {
- console.debug("fast algo");
return new Promise((resolve, reject) => {
let webWorkerURL = URL.createObjectURL(new Blob([
'(', processTask(), ')()'
@@ -99,7 +98,6 @@ function processTask() {
if (valid) {
hash = uint8ArrayToHexString(thisHash);
- console.log(hash);
break;
}
diff --git a/web/js/algos/sha256.mjs b/web/js/algos/sha256.mjs
new file mode 100644
index 0000000..cfea876
--- /dev/null
+++ b/web/js/algos/sha256.mjs
@@ -0,0 +1,160 @@
+import { u } from "../xeact.mjs";
+
+export default function process(
+ data,
+ difficulty = 16,
+ signal = null,
+ pc = null,
+ threads = (navigator.hardwareConcurrency || 1),
+) {
+ return new Promise(async (resolve, reject) => {
+ let webWorkerURL = URL.createObjectURL(new Blob([
+ '(', processTask(), ')()'
+ ], { type: 'application/javascript' }));
+
+ const module = await fetch(u("/.within.website/x/cmd/anubis/static/wasm/sha256.wasm"))
+ .then(resp => WebAssembly.compileStreaming(resp));
+
+ const workers = [];
+ const terminate = () => {
+ workers.forEach((w) => w.terminate());
+ if (signal != null) {
+ // clean up listener to avoid memory leak
+ signal.removeEventListener("abort", terminate);
+ if (signal.aborted) {
+ console.log("PoW aborted");
+ reject(false);
+ }
+ }
+ };
+ if (signal != null) {
+ signal.addEventListener("abort", terminate, { once: true });
+ }
+
+ for (let i = 0; i < threads; i++) {
+ let worker = new Worker(webWorkerURL);
+
+ worker.onmessage = (event) => {
+ if (typeof event.data === "number") {
+ pc?.(event.data);
+ } else {
+ terminate();
+ resolve(event.data);
+ }
+ };
+
+ worker.onerror = (event) => {
+ terminate();
+ reject(event);
+ };
+
+ worker.postMessage({
+ data,
+ difficulty,
+ nonce: i,
+ threads,
+ module,
+ });
+
+ workers.push(worker);
+ }
+
+ URL.revokeObjectURL(webWorkerURL);
+ });
+}
+
+function processTask() {
+ return function () {
+ addEventListener('message', async (event) => {
+ const importObject = {
+ anubis: {
+ anubis_update_nonce: (nonce) => postMessage(nonce),
+ }
+ };
+
+ const instance = await WebAssembly.instantiate(event.data.module, importObject);
+
+ // Get exports
+ const {
+ anubis_work,
+ data_ptr,
+ result_hash_ptr,
+ result_hash_size,
+ set_data_length,
+ memory
+ } = instance.exports;
+
+ function uint8ArrayToHex(arr) {
+ return Array.from(arr)
+ .map((c) => c.toString(16).padStart(2, "0"))
+ .join("");
+ }
+
+ function hexToUint8Array(hexString) {
+ // Remove whitespace and optional '0x' prefix
+ hexString = hexString.replace(/\s+/g, '').replace(/^0x/, '');
+
+ // Check for valid length
+ if (hexString.length % 2 !== 0) {
+ throw new Error('Invalid hex string length');
+ }
+
+ // Check for valid characters
+ if (!/^[0-9a-fA-F]+$/.test(hexString)) {
+ throw new Error('Invalid hex characters');
+ }
+
+ // Convert to Uint8Array
+ const byteArray = new Uint8Array(hexString.length / 2);
+ for (let i = 0; i < byteArray.length; i++) {
+ const byteValue = parseInt(hexString.substr(i * 2, 2), 16);
+ byteArray[i] = byteValue;
+ }
+
+ return byteArray;
+ }
+
+ // Write data to buffer
+ function writeToBuffer(data) {
+ if (data.length > 1024) throw new Error("Data exceeds buffer size");
+
+ // Get pointer and create view
+ const offset = data_ptr();
+ const buffer = new Uint8Array(memory.buffer, offset, data.length);
+
+ // Copy data
+ buffer.set(data);
+
+ // Set data length
+ set_data_length(data.length);
+ }
+
+ function readFromChallenge() {
+ const offset = result_hash_ptr();
+ const buffer = new Uint8Array(memory.buffer, offset, result_hash_size());
+
+ return buffer;
+ }
+
+ let data = event.data.data;
+ let difficulty = event.data.difficulty;
+ let hash;
+ let nonce = event.data.nonce;
+ let interand = event.data.threads;
+
+ writeToBuffer(hexToUint8Array(data));
+
+ nonce = anubis_work(difficulty, nonce, interand);
+ const challenge = readFromChallenge();
+
+ data = uint8ArrayToHex(challenge);
+
+ postMessage({
+ hash: data,
+ difficulty,
+ nonce,
+ });
+ });
+ }.toString();
+}
+
diff --git a/web/js/proof-of-work-slow.mjs b/web/js/algos/slow.mjs
index 0bdc146..2304859 100644
--- a/web/js/proof-of-work-slow.mjs
+++ b/web/js/algos/slow.mjs
@@ -7,7 +7,6 @@ export default function process(
progressCallback = null,
_threads = 1,
) {
- console.debug("slow algo");
return new Promise((resolve, reject) => {
let webWorkerURL = URL.createObjectURL(new Blob([
'(', processTask(), ')()'
diff --git a/web/js/bench.mjs b/web/js/bench.mjs
index c8c69bd..93daa90 100644
--- a/web/js/bench.mjs
+++ b/web/js/bench.mjs
@@ -1,10 +1,12 @@
-import processFast from "./proof-of-work.mjs";
-import processSlow from "./proof-of-work-slow.mjs";
+import fast from "./algos/fast.mjs";
+import slow from "./algos/slow.mjs";
+import sha256 from "./algos/sha256.mjs";
-const defaultDifficulty = 4;
+const defaultDifficulty = 16;
const algorithms = {
- fast: processFast,
- slow: processSlow,
+ sha256: sha256,
+ fast: fast,
+ slow: slow,
};
const status = document.getElementById("status");
@@ -41,10 +43,13 @@ const benchmarkTrial = async (stats, difficulty, algorithm, signal) => {
.map((c) => c.toString(16).padStart(2, "0"))
.join("");
+ if (algorithm != "sha256") {
+ difficulty = Math.round(difficulty / 4);
+ }
+
const t0 = performance.now();
const { hash, nonce } = await process(challenge, Number(difficulty), signal);
const t1 = performance.now();
- console.log({ hash, nonce });
stats.time += t1 - t0;
stats.iters += nonce;
diff --git a/web/js/main.mjs b/web/js/main.mjs
index a093c74..79d62e9 100644
--- a/web/js/main.mjs
+++ b/web/js/main.mjs
@@ -1,17 +1,13 @@
-import processFast from "./proof-of-work.mjs";
-import processSlow from "./proof-of-work-slow.mjs";
+import fast from "./algos/fast.mjs";
+import slow from "./algos/slow.mjs";
+import sha256 from "./algos/sha256.mjs";
import { testVideo } from "./video.mjs";
+import { u } from "./xeact.mjs";
const algorithms = {
- "fast": processFast,
- "slow": processSlow,
-};
-
-// from Xeact
-const u = (url = "", params = {}) => {
- let result = new URL(url, window.location.href);
- Object.entries(params).forEach(([k, v]) => result.searchParams.set(k, v));
- return result.toString();
+ "fast": fast,
+ "slow": slow,
+ "sha256": sha256,
};
const imageURL = (mood, cacheBuster) =>
@@ -28,6 +24,11 @@ const dependencies = [
msg: "Your browser doesn't support web workers (Anubis uses this to avoid freezing your browser). Do you have a plugin like JShelter installed?",
value: window.Worker,
},
+ {
+ name: "WebAssembly",
+ msg: "Your browser doesn't have WebAssembly support. If you are running a big endian system, I'm sorry but this is something we can't work around with a polyfill.",
+ value: window.WebAssembly,
+ },
];
function showContinueBar(hash, nonce, t0, t1) {
diff --git a/web/js/xeact.mjs b/web/js/xeact.mjs
new file mode 100644
index 0000000..5d5e8e3
--- /dev/null
+++ b/web/js/xeact.mjs
@@ -0,0 +1,13 @@
+/**
+ * Generate a relative URL from `url`, appending all key-value pairs from `params` as URL-encoded parameters.
+ *
+ * @type{function(string=, Object=): string}
+ */
+export const u = (url = "", params = {}) => {
+ let result = new URL(url, window.location.href);
+ Object.entries(params).forEach((kv) => {
+ let [k, v] = kv;
+ result.searchParams.set(k, v);
+ });
+ return result.toString();
+}; \ No newline at end of file
diff --git a/web/static/wasm/.gitignore b/web/static/wasm/.gitignore
new file mode 100644
index 0000000..c96a04f
--- /dev/null
+++ b/web/static/wasm/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore \ No newline at end of file