From 05aff8dbd17e00cc059db560f801c81ce5f62294 Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Sun, 23 Mar 2025 00:31:12 -0700 Subject: [PATCH] Implement lazy initialization of both APIs. Parse CLI output with regex and try to print as JSON before falling back to text. Fix CLI statements that were causing issues with older versions of node. Fix getter type. --- src/bin/cli.ts | 25 +++++++++++++++++-------- src/lib/gl/index.ts | 14 ++++++++------ src/lib/gpu/index.ts | 6 ++++++ src/lib/index.ts | 8 ++++---- src/types.d.ts | 2 +- 5 files changed, 36 insertions(+), 19 deletions(-) diff --git a/src/bin/cli.ts b/src/bin/cli.ts index 138c742..54c2b71 100755 --- a/src/bin/cli.ts +++ b/src/bin/cli.ts @@ -2,7 +2,7 @@ //! SPDX-FileCopyrightText: 2025 Chris Duncan //! SPDX-License-Identifier: GPL-3.0-or-later /// - +import * as crypto from 'node:crypto' import * as fs from 'node:fs/promises' import * as readline from 'node:readline/promises' import * as puppeteer from 'puppeteer' @@ -124,8 +124,9 @@ if (hashes.length === 0) { ] }) const page = await browser.newPage() - const cliPage = `${import.meta.dirname}/cli.html` - await fs.writeFile(cliPage, '') + const path: string = new URL(import.meta.url).pathname + const dir = path.slice(0, path.lastIndexOf('/')) + await fs.writeFile(`${dir}/cli.html`, '') await page.goto(import.meta.resolve('./cli.html')) await page.waitForFunction(async () => { return await navigator.gpu.requestAdapter() @@ -151,8 +152,8 @@ if (hashes.length === 0) { let start = performance.now() page.on('console', async (msg) => { - const output = msg.text().split(' ') - if (output[0] === 'cli') { + const output = msg.text().split('/^cli /') + if (output[0] === '') { if (output[1] === 'exit') { if (isJson) { const results = await page.evaluate(() => { @@ -170,10 +171,18 @@ if (hashes.length === 0) { if (options['debug']) console.log(end - start, 'ms total |', (end - start) / hashes.length, 'ms avg') await browser.close() } else if (!isJson) { - console.log(output[1]) + try { + console.log(JSON.parse(output[1])) + } catch (err) { + console.log(output[1]) + } } } else if (options['debug']) { - console.log(msg.text()) + try { + console.log(JSON.parse(msg.text())) + } catch (err) { + console.log(msg.text()) + } } }) start = performance.now() @@ -185,6 +194,6 @@ if (hashes.length === 0) { `) - await fs.unlink(cliPage) + await fs.unlink(`${dir}/cli.html`) if (options['debug']) console.log('Puppeteer initialized') })() diff --git a/src/lib/gl/index.ts b/src/lib/gl/index.ts index a1d45ee..016a3d5 100644 --- a/src/lib/gl/index.ts +++ b/src/lib/gl/index.ts @@ -14,6 +14,7 @@ export class NanoPowGl { static #SEND: bigint = 0xfffffff800000000n static #RECEIVE: bigint = 0xfffffe0000000000n + static #isInitialized: boolean = false static #busy: boolean = false static #debug: boolean = false static #raf: number = 0 @@ -177,12 +178,13 @@ export class NanoPowGl { /** Finalize configuration */ this.#query = this.#gl.createQuery() this.#pixels = new Uint32Array(this.size * 4) - console.log(`NanoPow WebGL initialized at ${this.#gl.drawingBufferWidth}x${this.#gl.drawingBufferHeight}. Maximum nonces checked per frame: ${this.size}`) } catch (err) { throw new Error('WebGL initialization failed.', { cause: err }) } finally { this.#busy = false } + this.#isInitialized = true + console.log(`NanoPow WebGL initialized at ${this.#gl.drawingBufferWidth}x${this.#gl.drawingBufferHeight}. Maximum nonces checked per frame: ${this.size}`) } /** @@ -370,6 +372,7 @@ export class NanoPowGl { * @param {NanoPowOptions} options - Options used to configure search execution */ static async work_generate (hash: string, options?: NanoPowOptions): Promise { + if (!/^[A-Fa-f0-9]{64}$/.test(hash)) throw new Error(`Invalid hash ${hash}`) if (this.#busy) { console.log('NanoPowGl is busy. Retrying search...') return new Promise(resolve => { @@ -379,10 +382,9 @@ export class NanoPowGl { }, 100) }) } + if (this.#isInitialized === false) this.init() this.#busy = true - /** Process user input */ - if (!/^[A-Fa-f0-9]{64}$/.test(hash)) throw new Error(`Invalid hash ${hash}`) const threshold = (typeof options?.threshold !== 'bigint' || options.threshold < 1n || options.threshold > 0xffffffffffffffffn) ? 0xfffffff800000000n : options.threshold @@ -479,6 +481,8 @@ export class NanoPowGl { * @param {NanoPowOptions} options - Options used to configure search execution */ static async work_validate (work: string, hash: string, options?: NanoPowOptions): Promise { + if (!/^[A-Fa-f0-9]{16}$/.test(work)) throw new Error(`Invalid work ${work}`) + if (!/^[A-Fa-f0-9]{64}$/.test(hash)) throw new Error(`Invalid hash ${hash}`) if (this.#busy) { console.log('NanoPowGl is busy. Retrying validate...') return new Promise(resolve => { @@ -488,11 +492,9 @@ export class NanoPowGl { }, 100) }) } + if (this.#isInitialized === false) this.init() this.#busy = true - /** Process user input */ - if (!/^[A-Fa-f0-9]{16}$/.test(work)) throw new Error(`Invalid work ${work}`) - if (!/^[A-Fa-f0-9]{64}$/.test(hash)) throw new Error(`Invalid hash ${hash}`) const threshold = (typeof options?.threshold !== 'bigint' || options.threshold < 1n || options.threshold > 0xffffffffffffffffn) ? 0xfffffff800000000n : options.threshold diff --git a/src/lib/gpu/index.ts b/src/lib/gpu/index.ts index 09f545e..6188d05 100644 --- a/src/lib/gpu/index.ts +++ b/src/lib/gpu/index.ts @@ -13,6 +13,7 @@ export class NanoPowGpu { static #RECEIVE: bigint = 0xfffffe0000000000n // Initialize WebGPU + static #isInitialized: boolean = false static #busy: boolean = false static #debug: boolean = false static #device: GPUDevice | null = null @@ -44,6 +45,7 @@ export class NanoPowGpu { } finally { this.#busy = false } + this.#isInitialized = true } static setup (): void { @@ -244,7 +246,9 @@ export class NanoPowGpu { }, 100) }) } + if (this.#isInitialized === false) this.init() this.#busy = true + const threshold = (typeof options?.threshold !== 'bigint' || options.threshold < 1n || options.threshold > 0xffffffffffffffffn) ? 0xfffffff800000000n : options.threshold @@ -331,7 +335,9 @@ export class NanoPowGpu { }, 100) }) } + if (this.#isInitialized === false) this.init() this.#busy = true + const threshold = (typeof options?.threshold !== 'bigint' || options.threshold < 1n || options.threshold > 0xffffffffffffffffn) ? 0xfffffff800000000n : options.threshold diff --git a/src/lib/index.ts b/src/lib/index.ts index b674851..a6edffb 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -6,15 +6,15 @@ import { NanoPowGpu } from "./gpu" let isGlSupported, isGpuSupported = false try { - await NanoPowGpu.init() - isGpuSupported = true + const adapter = await navigator?.gpu?.requestAdapter?.() + isGpuSupported = (adapter instanceof GPUAdapter) } catch (err) { console.warn('WebGPU is not supported in this environment.\n', err) isGpuSupported = false } try { - await NanoPowGl.init() - isGlSupported = true + const gl = new OffscreenCanvas(0, 0)?.getContext?.('webgl2') + isGlSupported = (gl instanceof WebGL2RenderingContext) } catch (err) { console.warn('WebGL is not supported in this environment.\n', err) isGlSupported = false diff --git a/src/types.d.ts b/src/types.d.ts index fc5aa59..02736d2 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -105,7 +105,7 @@ export type NanoPowOptions = { export declare class NanoPowGl { #private /** Drawing buffer width in pixels. */ - static get size (): number | undefined + static get size (): number /** * Constructs canvas, gets WebGL context, initializes buffers, and compiles * shaders. -- 2.34.1