: options.effort
this.#debug = !!(options?.debug)
-
- 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'))
- })
+ 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')
}
/**
+++ /dev/null
-// SPDX-FileCopyrightText: 2024 Chris Duncan <chris@zoso.dev>
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-type Job = {
- id: number
- reject: (value: any) => void
- resolve: (value: any) => void
- data: any
- results: any[]
-}
-
-type Thread = {
- worker: Worker
- job: Job | null
-}
-
-/**
-* Processes an array of tasks using Web Workers.
-*/
-export class Pool {
- static #cores: number = Math.max(1, navigator.hardwareConcurrency - 1)
- #queue: Job[] = []
- #threads: Thread[] = []
- #url: string
-
- get threadsBusy (): number {
- let n = 0
- for (const thread of this.#threads) {
- n += +(thread.job != null)
- }
- return n
- }
- get threadsIdle (): number {
- let n = 0
- for (const thread of this.#threads) {
- n += +(thread.job == null)
- }
- return n
- }
-
- async assign (data: any): Promise<any> {
- if (!(data instanceof ArrayBuffer || Array.isArray(data))) data = [data]
- return new Promise((resolve, reject) => {
- const job: Job = {
- id: performance.now(),
- results: [],
- data,
- resolve,
- reject
- }
- if (this.#queue.length > 0) {
- this.#queue.push(job)
- } else {
- for (const thread of this.#threads) this.#assign(thread, job)
- }
- })
- }
-
- /**
- *
- * @param {string} worker - Stringified worker class
- * @param {number} [count=1] - Integer between 1 and CPU thread count shared among all Pools
- */
- constructor (worker: string, count: number = 1) {
- count = Math.min(Pool.#cores, Math.max(1, Math.floor(Math.abs(count))))
- this.#url = URL.createObjectURL(new Blob([worker], { type: 'text/javascript' }))
- for (let i = 0; i < count; i++) {
- const thread = {
- worker: new Worker(this.#url, { type: 'module' }),
- job: null
- }
- thread.worker.addEventListener('message', message => {
- let result = JSON.parse(new TextDecoder().decode(message.data) || "[]")
- if (!Array.isArray(result)) result = [result]
- this.#report(thread, result)
- })
- this.#threads.push(thread)
- Pool.#cores = Math.max(1, Pool.#cores - this.#threads.length)
- }
- }
-
- #assign (thread: Thread, job: Job): void {
- if (job.data instanceof ArrayBuffer) {
- if (job.data.byteLength > 0) {
- thread.job = job
- thread.worker.postMessage({ buffer: job.data }, [job.data])
- }
- } else {
- const chunk: number = 1 + (job.data.length / this.threadsIdle)
- const next = job.data.slice(0, chunk)
- job.data = job.data.slice(chunk)
- if (job.data.length === 0) this.#queue.shift()
- if (next?.length > 0) {
- const buffer = new TextEncoder().encode(JSON.stringify(next)).buffer
- thread.job = job
- thread.worker.postMessage({ buffer }, [buffer])
- }
- }
- }
-
- #isJobDone (jobId: number): boolean {
- for (const thread of this.#threads) {
- if (thread.job?.id === jobId) return false
- }
- return true
- }
-
- #report (thread: Thread, results: any[]): void {
- if (thread.job == null) {
- throw new Error('Thread returned results but had nowhere to report it.')
- }
- const job = thread.job
- if (this.#queue.length > 0) {
- this.#assign(thread, this.#queue[0])
- } else {
- thread.job = null
- }
- if (results.length > 0) {
- job.results.push(...results)
- }
- if (this.#isJobDone(job.id)) {
- job.resolve(job.results)
- }
- }
-}
-
-/**
-* Provides basic worker event messaging to extending classes.
-*
-* In order to be properly bundled in a format that can be used to create an
-* inline Web Worker, the extending classes must export WorkerInterface and
-* themselves as a string:
-*```
-* export default `
-* const WorkerInterface = ${WorkerInterface}
-* const Pow = ${Pow}
-* `
-* ```
-* They must also initialize the event listener by calling their inherited
-* `listen()` function. Finally, they must override the implementation of the
-* `work()` function. See the documentation of those functions for details.
-*/
+++ /dev/null
-// SPDX-FileCopyrightText: 2024 Chris Duncan <chris@zoso.dev>
-// SPDX-License-Identifier: GPL-3.0-or-later
-import { WorkerInterface } from '#~/pool.js'
-import { NanoPow } from 'nano-pow'
-
-/**
-* Nano proof-of-work using WebGPU and Web Workers.
-*/
-export class NanoPowWorker extends WorkerInterface {
- static {
- NanoPowWorker.listen()
- }
-
- /**
- * Calculates proof-of-work as described by the Nano cryptocurrency protocol.
- *
- * @param {any[]} data - Array of hashes and minimum thresholds
- * @returns Promise for proof-of-work attached to original array objects
- */
- static async work (data: any[]): Promise<any[]> {
- 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)
- })
- }
-}
-
-export default `
- const NanoPow = ${NanoPow}
- const WorkerInterface = ${WorkerInterface}
- const NanoPowWorker = ${NanoPowWorker}
-`