From f5ad518519b5d788c3ab33261083e03b862e9e86 Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Mon, 3 Mar 2025 20:29:13 -0800 Subject: [PATCH] Continue refactoring CPU to use workers. --- src/classes/cpu.ts | 47 ++++++++++----- src/classes/pow-pool.ts | 61 ------------------- src/shaders/cpu-hash.ts | 129 ++++++++++++++++++++++++++-------------- 3 files changed, 118 insertions(+), 119 deletions(-) diff --git a/src/classes/cpu.ts b/src/classes/cpu.ts index 4a7c2d4..d6e9336 100644 --- a/src/classes/cpu.ts +++ b/src/classes/cpu.ts @@ -1,6 +1,5 @@ // SPDX-FileCopyrightText: 2025 Chris Duncan // SPDX-License-Identifier: GPL-3.0-or-later -/// import { NanoPowCpuHasher } from '../shaders' import type { NanoPowOptions } from '../../types.d.ts' @@ -10,6 +9,7 @@ import type { NanoPowOptions } from '../../types.d.ts' export class NanoPowCpu { // Initialize constants and buffers static #busy: boolean = false + static #cores: number = Math.max(1, Math.floor(navigator.hardwareConcurrency)) static #debug: boolean = false static #blake2b_IV: BigUint64Array static #blake2b_sigma: number[][] @@ -167,22 +167,39 @@ export class NanoPowCpu { const threshold = (typeof options?.threshold !== 'number' || options.threshold < 0x0 || options.threshold > 0xffffffff) ? 0xfffffff800000000n : BigInt(`0x${options.threshold}00000000`) + const effort = (typeof options?.effort !== 'number' || options.effort < 0x1 || options.effort > 0x20) + ? this.#cores + : options.effort this.#debug = !!(options?.debug) - let attempts = 0n - let result = 0n - let nonce = 0n - let start = performance.now() - do { - attempts++ - const random0 = Math.floor(Math.random() * 0xffffffff) - const random1 = Math.floor(Math.random() * 0xffffffff) - nonce = (BigInt(random0) << 32n) | BigInt(random1) - result = this.#hash(nonce, hash) - } while (result < threshold) - console.log(performance.now() - start) - this.#busy = false - return result.toString(16).padStart(16, '0') + + return new Promise(async (resolve, reject): Promise => { + if (NanoPow == null) throw new Error('NanoPow not available') + for (const d of data) { + try { + d.work = await NanoPow.search(d.hash, d.threshold) + } catch (err) { + reject(err) + } + } + resolve(data) + + + let attempts = 0n + let result = 0n + let nonce = 0n + let start = performance.now() + do { + attempts++ + const random0 = Math.floor(Math.random() * 0xffffffff) + const random1 = Math.floor(Math.random() * 0xffffffff) + nonce = (BigInt(random0) << 32n) | BigInt(random1) + result = this.#hash(nonce, hash) + } while (result < threshold) + console.log(performance.now() - start) + this.#busy = false + resolve(result.toString(16).padStart(16, '0')) + }) } /** diff --git a/src/classes/pow-pool.ts b/src/classes/pow-pool.ts index d763dff..2df8b34 100644 --- a/src/classes/pow-pool.ts +++ b/src/classes/pow-pool.ts @@ -140,64 +140,3 @@ export class Pool { * `listen()` function. Finally, they must override the implementation of the * `work()` function. See the documentation of those functions for details. */ -export class WorkerInterface { - /** - * Processes data through a worker. - * - * Extending classes must override this template by implementing the same - * function signature and providing their own processing call in the try-catch - * block. - * - * @param {any[]} data - Array of data to process - * @returns Promise for that data after being processed - */ - static async work (data: any[]): Promise { - return new Promise(async (resolve, reject): Promise => { - for (let d of data) { - try { - d = await d - } catch (err) { - reject(err) - } - } - resolve(data) - }) - } - - /** - * Encodes worker results as an ArrayBuffer so it can be transferred back to - * the main thread. - * - * @param {any[]} results - Array of processed data - */ - static report (results: any[]): void { - const buffer = new TextEncoder().encode(JSON.stringify(results)).buffer - //@ts-expect-error - postMessage(buffer, [buffer]) - } - - /** - * Listens for messages from the main thread. - * - * Extending classes must call this in a static initialization block: - * ``` - * static { - * Pow.listen() - * } - * ``` - */ - static listen (): void { - addEventListener('message', (message: any): void => { - const { name, buffer } = message.data - if (name === 'STOP') { - close() - const buffer = new ArrayBuffer(0) - //@ts-expect-error - postMessage(buffer, [buffer]) - } else { - const data = JSON.parse(new TextDecoder().decode(buffer)) - this.work(data).then(this.report) - } - }) - } -} diff --git a/src/shaders/cpu-hash.ts b/src/shaders/cpu-hash.ts index 775b489..5cf927c 100644 --- a/src/shaders/cpu-hash.ts +++ b/src/shaders/cpu-hash.ts @@ -1,48 +1,91 @@ -export const NanoPowCpuHasher = ` -const blake2b_sigma = [ - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], - [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], - [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], - [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], - [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], - [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], - [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], - [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], - [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], - [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], - [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3] -] +// SPDX-FileCopyrightText: 2025 Chris Duncan +// SPDX-License-Identifier: GPL-3.0-or-later -function G (v, a, b, c, d, m, x, y) { - v[a] += v[b] - v[a] += m[x] - v[d] ^= v[a] - v[d] = (v[d] >> 32n) | (v[d] << 32n) - v[c] += v[d] - v[b] ^= v[c] - v[b] = (v[b] >> 24n) | (v[b] << 40n) - v[a] += v[b] - v[a] += m[y] - v[d] ^= v[a] - v[d] = (v[d] >> 16n) | (v[d] << 48n) - v[c] += v[d] - v[b] ^= v[c] - v[b] = (v[b] >> 63n) | (v[b] << 1n) -} +export class NanoPowCpuHasher { + static #blake2b_sigma: number[][] = [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], + [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], + [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], + [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], + [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], + [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], + [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], + [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], + [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3] + ] + + static #G (v: BigUint64Array, a: number, b: number, c: number, d: number, m: BigUint64Array, x: number, y: number): void { + v[a] += v[b] + v[a] += m[x] + v[d] ^= v[a] + v[d] = (v[d] >> 32n) | (v[d] << 32n) + v[c] += v[d] + v[b] ^= v[c] + v[b] = (v[b] >> 24n) | (v[b] << 40n) + v[a] += v[b] + v[a] += m[y] + v[d] ^= v[a] + v[d] = (v[d] >> 16n) | (v[d] << 48n) + v[c] += v[d] + v[b] ^= v[c] + v[b] = (v[b] >> 63n) | (v[b] << 1n) + } + + static async hash (v: BigUint64Array, m: BigUint64Array): Promise { + return new Promise(async (resolve, reject): Promise => { + try { + for (let r = 0; r < 12; r++) { + const s = this.#blake2b_sigma[r] + this.#G(v, 0, 4, 8, 12, m, s[0], s[1]) + this.#G(v, 1, 5, 9, 13, m, s[2], s[3]) + this.#G(v, 2, 6, 10, 14, m, s[4], s[5]) + this.#G(v, 3, 7, 11, 15, m, s[6], s[7]) + this.#G(v, 0, 5, 10, 15, m, s[8], s[9]) + this.#G(v, 1, 6, 11, 12, m, s[10], s[11]) + this.#G(v, 2, 7, 8, 13, m, s[12], s[13]) + this.#G(v, 3, 4, 9, 14, m, s[14], s[15]) + } + } catch (err) { + reject(err) + } + resolve(0x6a09e667f3bcc908n ^ v[0] ^ v[8]) + }) + } + + /** + * Listens for messages from the main thread. + */ + static { + addEventListener('message', (message: any): void => { + const { name, buffer } = message.data + if (name === 'STOP') { + close() + const buffer = new ArrayBuffer(0) + //@ts-expect-error + postMessage(buffer, [buffer]) + } else { + const data = JSON.parse(new TextDecoder().decode(buffer)) + this.hash(data).then(this.report) + } + }) + } -function hash (m, v) { - for (let r = 0; r < 12; r++) { - const s = blake2b_sigma[r] - G(v, 0, 4, 8, 12, m, s[0], s[1]) - G(v, 1, 5, 9, 13, m, s[2], s[3]) - G(v, 2, 6, 10, 14, m, s[4], s[5]) - G(v, 3, 7, 11, 15, m, s[6], s[7]) - G(v, 0, 5, 10, 15, m, s[8], s[9]) - G(v, 1, 6, 11, 12, m, s[10], s[11]) - G(v, 2, 7, 8, 13, m, s[12], s[13]) - G(v, 3, 4, 9, 14, m, s[14], s[15]) + /** + * Encodes worker results as an ArrayBuffer so it can be transferred back to + * the main thread. + * + * @param {bigint} nonce - 64-bit nonce + */ + static report (nonce: bigint): void { + const buffer = new TextEncoder().encode(JSON.stringify(nonce)).buffer + //@ts-expect-error + postMessage(buffer, [buffer]) } - return (0x6a09e667f3bcc908 ^ v[0] ^ v[8]) } + +export default ` +const NanoPowCpuHasher = ${NanoPowCpuHasher} ` -- 2.34.1