]> git.codecow.com Git - nano-pow.git/commitdiff
Implement lazy initialization of both APIs. Parse CLI output with regex and try to...
authorChris Duncan <chris@zoso.dev>
Sun, 23 Mar 2025 07:31:12 +0000 (00:31 -0700)
committerChris Duncan <chris@zoso.dev>
Sun, 23 Mar 2025 07:31:12 +0000 (00:31 -0700)
src/bin/cli.ts
src/lib/gl/index.ts
src/lib/gpu/index.ts
src/lib/index.ts
src/types.d.ts

index 138c7425fce8c6fdf2714349794a0072a9fa7222..54c2b71f736914aa3334fce17d3943ea02c4ebbd 100755 (executable)
@@ -2,7 +2,7 @@
 //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
 //! SPDX-License-Identifier: GPL-3.0-or-later
 /// <reference types="@webgpu/types" />
-
+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) {
                        </head>
                </html>
        `)
-       await fs.unlink(cliPage)
+       await fs.unlink(`${dir}/cli.html`)
        if (options['debug']) console.log('Puppeteer initialized')
 })()
index a1d45eefde84a87ddd9689bb9c6e30e2ae584092..016a3d5d420bf6af1e0cddef146a0e792ade2339 100644 (file)
@@ -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<WorkGenerateResponse> {
+               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<WorkValidateResponse> {
+               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
index 09f545e48d36cb60793357b2e2c748ab5cd82dfa..6188d05732554b750c4104615a169c601f44954e 100644 (file)
@@ -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
index b674851450c55060c65dc5376bf2b7536aad60b4..a6edffb2f60f5127266b8ea602948c87f1ec319f 100644 (file)
@@ -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
index fc5aa59f2c28bce58ff8eed93083d2d3bcbb12d3..02736d2b42a60ccbbbf85bcfd32cd92c8db85ac8 100644 (file)
@@ -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.