]> zoso.dev Git - nano-pow.git/commitdiff
Continue refactoring CPU to use workers.
authorChris Duncan <chris@zoso.dev>
Tue, 4 Mar 2025 04:29:13 +0000 (20:29 -0800)
committerChris Duncan <chris@zoso.dev>
Tue, 4 Mar 2025 04:29:13 +0000 (20:29 -0800)
src/classes/cpu.ts
src/classes/pow-pool.ts
src/shaders/cpu-hash.ts

index 4a7c2d4bb3967b9ec17b9b6da29eee9b78c58a1f..d6e9336a51646914521aeace35cdc4fad28ebfc9 100644 (file)
@@ -1,6 +1,5 @@
 // SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
 // SPDX-License-Identifier: GPL-3.0-or-later
-/// <reference types="@webgpu/types" />
 
 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<void> => {
+                       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'))
+               })
        }
 
        /**
index d763dff0718dd0e7b73b93ebb079d0827506289e..2df8b3454f452be9eaf1ad6e597a72cde73ef6d8 100644 (file)
@@ -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<any[]> {
-               return new Promise(async (resolve, reject): Promise<void> => {
-                       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)
-                       }
-               })
-       }
-}
index 775b4898bbd3c1ce53ea7d5c3067f045cc29efd5..5cf927c874a603335a09958242ec8d0e06f13454 100644 (file)
@@ -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 <chris@zoso.dev>
+// 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<bigint> {
+               return new Promise(async (resolve, reject): Promise<void> => {
+                       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}
 `