diff options
| author | Xe Iaso <me@xeiaso.net> | 2025-04-09 00:12:38 -0400 |
|---|---|---|
| committer | Xe Iaso <me@xeiaso.net> | 2025-04-09 00:12:38 -0400 |
| commit | cc1d5b71da0cbc79c3f84a2f59d3daa38d1a06e6 (patch) | |
| tree | 7d6ce1d0d950809a00caa50af60ac146afffb952 /web | |
| parent | 2324395ae2dcdb4729f9c29e393a6a03a6e84af6 (diff) | |
| download | anubis-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.mjs | 160 | ||||
| -rw-r--r-- | web/js/algos/slow.mjs (renamed from web/js/proof-of-work-slow.mjs) | 1 | ||||
| -rw-r--r-- | web/js/bench.mjs | 17 | ||||
| -rw-r--r-- | web/js/main.mjs | 23 | ||||
| -rw-r--r-- | web/js/xeact.mjs | 13 | ||||
| -rw-r--r-- | web/static/wasm/.gitignore | 2 |
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 |
