]> zoso.dev Git - nano-pow.git/commitdiff
Initial merge of work call shared code. Runs terribly, needs troubleshooting.
authorChris Duncan <chris@zoso.dev>
Wed, 23 Apr 2025 21:38:29 +0000 (14:38 -0700)
committerChris Duncan <chris@zoso.dev>
Wed, 23 Apr 2025 21:38:29 +0000 (14:38 -0700)
src/lib/gpu/index.ts

index b9a8b13db7e3e482bd3ee0666b14a5659f3c8eaf..0ec6119bdf3410ee1f4e0c2808d515d8f622dc06 100644 (file)
@@ -29,6 +29,7 @@ export class NanoPowGpu {
 
        // Initialize WebGPU
        static async init (): Promise<void> {
+               console.log('Initializing NanoPowGpu.')
                if (this.#busy) return
                this.#busy = true
                // Request device and adapter
@@ -44,9 +45,9 @@ export class NanoPowGpu {
                } catch (err) {
                        throw new Error('WebGPU initialization failed.', { cause: err })
                } finally {
+                       this.#isInitialized = true
                        this.#busy = false
                }
-               this.#isInitialized = true
        }
 
        static async setup (): Promise<void> {
@@ -171,6 +172,70 @@ export class NanoPowGpu {
                console.table(averages)
        }
 
+       /**
+        * Validate work, if present, and blockhash.
+        * Validate options and normalize its properties.
+        */
+       static async #work_init (work: string | null, hash: string, options?: NanoPowOptions) {
+               if (this.#busy) {
+                       console.log('NanoPowGpu is busy. Retrying search...')
+                       return new Promise(r => {
+                               setTimeout(async () => {
+                                       r(this.#work_init(work, hash, options))
+                               }, 100)
+                       }) as Promise<{ difficulty: any, effort: any }>
+               }
+               if (this.#isInitialized === false) this.init()
+               this.#busy = true
+
+               if (!/^[A-Fa-f0-9]{64}$/.test(hash)) throw new TypeError(`Invalid hash ${hash}`)
+               if (work != null && !/^[A-Fa-f0-9]{16}$/.test(work)) throw new TypeError(`Invalid work ${work}`)
+               options ??= {}
+               options.debug ??= false
+               options.difficulty ??= 0xfffffff800000000n
+               options.effort ??= 0x4
+
+               if (typeof options.effort !== 'number'
+                       && (options.effort < 0x1 || options.effort > 0x20)
+               ) {
+                       throw new TypeError(`Invalid effort ${options.effort}`)
+               }
+               const effort = options.effort
+
+               if (typeof options.difficulty !== 'string'
+                       && typeof options.difficulty !== 'bigint'
+               ) {
+                       throw new TypeError(`Invalid difficulty ${options.difficulty}`)
+               }
+               const difficulty = typeof options.difficulty === 'string'
+                       ? BigInt(`0x${options.difficulty}`)
+                       : options.difficulty
+
+               if (difficulty < 0x0n || difficulty > 0xfffffff800000000n) {
+                       throw new TypeError(`Invalid difficulty ${options.difficulty}`)
+               }
+
+               this.#debug = !!options.debug
+               if (this.#debug) {
+                       console.log('blockhash', hash)
+                       console.log(`options`, JSON.stringify(options, (k, v) => typeof v === 'bigint' ? v.toString(16) : v))
+               }
+
+               // Ensure WebGPU is initialized before calculating
+               let loads = 0
+               while (this.#device == null && loads++ < 20) {
+                       await new Promise(resolve => {
+                               setTimeout(resolve, 500)
+                       })
+               }
+               if (this.#device == null) {
+                       this.#busy = false
+                       throw new Error(`WebGPU device failed to load.`)
+               }
+
+               return { difficulty, effort }
+       }
+
        static async #dispatch (pipeline: GPUComputePipeline, seed: bigint, hash: string, difficulty: bigint, passes: number): Promise<void> {
                if (this.#device == null) throw new Error(`WebGPU device failed to load.`)
                // Set up uniform buffer object
@@ -227,48 +292,7 @@ export class NanoPowGpu {
        * @param {NanoPowOptions} 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 TypeError(`Invalid hash ${hash}`)
-               if (this.#busy) {
-                       console.log('NanoPowGpu is busy. Retrying search...')
-                       return new Promise(resolve => {
-                               setTimeout(async (): Promise<void> => {
-                                       const result = this.work_generate(hash, options)
-                                       resolve(result)
-                               }, 100)
-                       })
-               }
-               if (this.#isInitialized === false) this.init()
-               this.#busy = true
-
-               if (typeof options?.difficulty === 'string') {
-                       try {
-                               options.difficulty = BigInt(`0x${options.difficulty}`)
-                       } catch (err) {
-                               throw new TypeError(`Invalid difficulty ${options.difficulty}`)
-                       }
-               }
-               const difficulty = (typeof options?.difficulty !== 'bigint' || options.difficulty < 0n || options.difficulty > 0xffffffffffffffffn)
-                       ? 0xfffffff800000000n
-                       : options.difficulty
-               const effort = (typeof options?.effort !== 'number' || options.effort < 0x1 || options.effort > 0x20)
-                       ? 0x800
-                       : options.effort * 0x100
-               this.#debug = !!(options?.debug)
-               if (this.#debug) console.log('NanoPowGpu.work_generate()')
-               if (this.#debug) console.log('blockhash', hash)
-               if (this.#debug) console.log('search options', JSON.stringify(options, (k, v) => typeof v === 'bigint' ? v.toString(16) : v))
-
-               // Ensure WebGPU is initialized before calculating
-               let loads = 0
-               while (this.#device == null && loads++ < 20) {
-                       await new Promise(resolve => {
-                               setTimeout(resolve, 500)
-                       })
-               }
-               if (this.#device == null) {
-                       this.#busy = false
-                       throw new Error(`WebGPU device failed to load.`)
-               }
+               const { difficulty, effort } = await this.#work_init(null, hash, options)
 
                let times = []
                let start = performance.now()
@@ -288,9 +312,13 @@ export class NanoPowGpu {
                        this.#busy = !found
                        times.push(performance.now() - start)
                } while (this.#busy)
-               if (this.#debug) this.#logAverages(times)
-               if (this.#debug) console.log('nonce', nonce, nonce.toString(16).padStart(16, '0'))
-               if (this.#debug) console.log('result', result, result.toString(16).padStart(16, '0'))
+
+               if (this.#debug) {
+                       this.#logAverages(times)
+                       console.log('nonce', nonce, nonce.toString(16).padStart(16, '0'))
+                       console.log('result', result, result.toString(16).padStart(16, '0'))
+               }
+
                return {
                        hash,
                        work: nonce.toString(16).padStart(16, '0'),
@@ -306,46 +334,7 @@ export class NanoPowGpu {
        * @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 TypeError(`Invalid work ${work}`)
-               if (!/^[A-Fa-f0-9]{64}$/.test(hash)) throw new TypeError(`Invalid hash ${hash}`)
-               if (this.#busy) {
-                       console.log('NanoPowGpu is busy. Retrying validate...')
-                       return new Promise(resolve => {
-                               setTimeout(async (): Promise<void> => {
-                                       const result = this.work_validate(work, hash, options)
-                                       resolve(result)
-                               }, 100)
-                       })
-               }
-               if (this.#isInitialized === false) this.init()
-               this.#busy = true
-
-               if (typeof options?.difficulty === 'string') {
-                       try {
-                               options.difficulty = BigInt(`0x${options.difficulty}`)
-                       } catch (err) {
-                               throw new TypeError(`Invalid difficulty ${options.difficulty}`)
-                       }
-               }
-               const difficulty = (typeof options?.difficulty !== 'bigint' || options.difficulty < 0n || options.difficulty > 0xffffffffffffffffn)
-                       ? 0xfffffff800000000n
-                       : options.difficulty
-               this.#debug = !!(options?.debug)
-               if (this.#debug) console.log('NanoPowGpu.work_validate()')
-               if (this.#debug) console.log('blockhash', hash)
-               if (this.#debug) console.log('validate options', JSON.stringify(options, (k, v) => typeof v === 'bigint' ? v.toString(16) : v))
-
-               // Ensure WebGPU is initialized before calculating
-               let loads = 0
-               while (this.#device == null && loads < 20) {
-                       await new Promise(resolve => {
-                               setTimeout(resolve, 500)
-                       })
-               }
-               if (this.#device == null) {
-                       this.#busy = false
-                       throw new Error(`WebGPU device failed to load.`)
-               }
+               const { difficulty } = await this.#work_init(work, hash, options)
 
                let result = 0n
                let nonce = 0n
@@ -357,16 +346,22 @@ export class NanoPowGpu {
                result = this.#resultView.getBigUint64(16, true)
                this.#busy = false
                if (seed !== nonce) throw new Error('Result does not match work')
-               if (this.#debug) console.log('nonce', nonce, nonce.toString(16).padStart(16, '0'))
-               if (this.#debug) console.log('result', result, result.toString(16).padStart(16, '0'))
+
+               if (this.#debug) {
+                       console.log('nonce', nonce, nonce.toString(16).padStart(16, '0'))
+                       console.log('result', result, result.toString(16).padStart(16, '0'))
+               }
+
                const response: WorkValidateResponse = {
                        hash,
                        work: nonce.toString(16).padStart(16, '0'),
                        difficulty: result.toString(16).padStart(16, '0'),
                        valid_all: (result >= this.#SEND) ? '1' : '0',
-                       valid_receive: (result >= this.#RECEIVE) ? '1' : '0',
+                       valid_receive: (result >= this.#RECEIVE) ? '1' : '0'
+               }
+               if (options?.difficulty != null) {
+                       response.valid = (result >= difficulty) ? '1' : '0'
                }
-               if (options?.difficulty != null) response.valid = (result >= difficulty) ? '1' : '0'
                return response
        }
 }