//! 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'
]
})
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()
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(() => {
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()
</head>
</html>
`)
- await fs.unlink(cliPage)
+ await fs.unlink(`${dir}/cli.html`)
if (options['debug']) console.log('Puppeteer initialized')
})()
static #SEND: bigint = 0xfffffff800000000n
static #RECEIVE: bigint = 0xfffffe0000000000n
+ static #isInitialized: boolean = false
static #busy: boolean = false
static #debug: boolean = false
static #raf: number = 0
/** 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}`)
}
/**
* @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 => {
}, 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
* @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 => {
}, 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
static #RECEIVE: bigint = 0xfffffe0000000000n
// Initialize WebGPU
+ static #isInitialized: boolean = false
static #busy: boolean = false
static #debug: boolean = false
static #device: GPUDevice | null = null
} finally {
this.#busy = false
}
+ this.#isInitialized = true
}
static setup (): void {
}, 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
}, 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
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
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.