]> zoso.dev Git - libnemo.git/commitdiff
Merge redundant find function with calculate to form search function like PowGpu...
authorChris Duncan <chris@zoso.dev>
Tue, 7 Jan 2025 20:32:19 +0000 (12:32 -0800)
committerChris Duncan <chris@zoso.dev>
Tue, 7 Jan 2025 20:32:19 +0000 (12:32 -0800)
src/lib/workers/powgl.ts

index 1311fb17b543ce1143a5d3e881e6aa6eda87bb5c..6eea95b58af41a522f0cf17be2c9386cb64bb907 100644 (file)
@@ -18,7 +18,7 @@ export class PowGl extends WorkerInterface {
                return new Promise(async (resolve, reject): Promise<void> => {
                        for (const d of data) {
                                try {
-                                       d.work = await this.find(d.hash, d.threshold)
+                                       d.work = await this.search(d.hash, d.threshold)
                                } catch (err) {
                                        reject(err)
                                }
@@ -27,18 +27,6 @@ export class PowGl extends WorkerInterface {
                })
        }
 
-       /**
-       * Finds a nonce that satisfies the Nano proof-of-work requirements.
-       *
-       * @param {string} hash - Hexadecimal hash of previous block, or public key for new accounts
-       * @param {number} [threshold=0xfffffff8] - Difficulty of proof-of-work calculation
-       */
-       static async find (hash: string, threshold: number = 0xfffffff8): Promise<string> {
-               return new Promise<string>(resolve => {
-                       this.#calculate(hash, resolve, threshold)
-               })
-       }
-
        // Vertex Shader
        static #vsSource = `#version 300 es
 #pragma vscode_glsllint_stage: vert
@@ -357,16 +345,22 @@ void main() {
                this.#query = this.#gl.createQuery()
        }
 
-       static #calculate (hashHex: string, callback: (nonce: string | PromiseLike<string>) => any, threshold: number): void {
+       /**
+       * Finds a nonce that satisfies the Nano proof-of-work requirements.
+       *
+       * @param {string} hash - Hexadecimal hash of previous block, or public key for new accounts
+       * @param {number} [threshold=0xfffffff8] - Difficulty of proof-of-work calculation
+       */
+       static async search (hash: string, threshold: number = 0xfffffff8): Promise<string> {
                if (PowGl.#gl == null) throw new Error('WebGL 2 is required')
-               if (!/^[A-F-a-f0-9]{64}$/.test(hashHex)) throw new Error(`invalid_hash ${hashHex}`)
+               if (!/^[A-F-a-f0-9]{64}$/.test(hash)) throw new Error(`invalid_hash ${hash}`)
                if (typeof threshold !== 'number') throw new TypeError(`Invalid threshold ${threshold}`)
                if (this.#gl == null) throw new Error('WebGL 2 is required')
 
                // Set up uniform buffer object
                const uboView = new DataView(new ArrayBuffer(144))
                for (let i = 0; i < 64; i += 8) {
-                       const uint32 = hashHex.slice(i, i + 8)
+                       const uint32 = hash.slice(i, i + 8)
                        uboView.setUint32(i * 2, parseInt(uint32, 16))
                }
                uboView.setUint32(128, threshold, true)
@@ -375,63 +369,77 @@ void main() {
                PowGl.#gl.bufferSubData(PowGl.#gl.UNIFORM_BUFFER, 0, uboView)
                PowGl.#gl.bindBuffer(PowGl.#gl.UNIFORM_BUFFER, null)
 
-               // Draw output until success or progressCallback says to stop
+               // Start drawing to calculate one nonce per pixel
+               let nonce = null
                const work = new Uint8Array(8)
-               let start: DOMHighResTimeStamp
-               const draw = (): void => {
-                       if (PowGl.#gl == null) throw new Error('WebGL 2 is required')
-                       if (PowGl.#query == null) throw new Error('WebGL 2 is required to run queries')
-                       PowGl.#gl.clear(PowGl.#gl.COLOR_BUFFER_BIT)
-
-                       // Upload work buffer
-                       crypto.getRandomValues(work)
-                       PowGl.#gl.bindBuffer(PowGl.#gl.UNIFORM_BUFFER, PowGl.#workBuffer)
-                       PowGl.#gl.bufferSubData(PowGl.#gl.UNIFORM_BUFFER, 0, Uint32Array.from(work))
-                       PowGl.#gl.bindBuffer(PowGl.#gl.UNIFORM_BUFFER, null)
-
-                       PowGl.#gl.beginQuery(PowGl.#gl.ANY_SAMPLES_PASSED_CONSERVATIVE, PowGl.#query)
-                       PowGl.#gl.drawArrays(PowGl.#gl.TRIANGLES, 0, 6)
-                       PowGl.#gl.endQuery(PowGl.#gl.ANY_SAMPLES_PASSED_CONSERVATIVE)
-
-                       requestAnimationFrame(checkQueryResult)
+               while (nonce == null) {
+                       this.draw(work)
+                       const found = await this.checkQueryResult()
+                       if (found) {
+                               nonce = this.readResult(work)
+                       }
                }
+               return nonce
+       }
 
-               function checkQueryResult () {
-                       if (PowGl.#gl == null) throw new Error('WebGL 2 is required to check query results')
-                       if (PowGl.#query == null) throw new Error('Query not found')
-                       if (PowGl.#gl.getQueryParameter(PowGl.#query, PowGl.#gl.QUERY_RESULT_AVAILABLE)) {
-                               const anySamplesPassed = PowGl.#gl.getQueryParameter(PowGl.#query, PowGl.#gl.QUERY_RESULT)
-                               if (anySamplesPassed) {
-                                       // A valid nonce was found
-                                       readBackResult()
-                               } else {
-                                       // No valid nonce found, start the next draw call
-                                       requestAnimationFrame(draw)
-                               }
-                       } else {
-                               // Query result not yet available, check again in the next frame
-                               requestAnimationFrame(checkQueryResult)
-                       }
+       static draw (work: Uint8Array): void {
+               if (this.#gl == null || this.#query == null) throw new Error('WebGL 2 is required to draw and query pixels')
+               if (this.#workBuffer == null) throw new Error('Work buffer is required to draw')
+               this.#gl.clear(this.#gl.COLOR_BUFFER_BIT)
+
+               // Upload work buffer
+               crypto.getRandomValues(work)
+               this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, this.#workBuffer)
+               this.#gl.bufferSubData(this.#gl.UNIFORM_BUFFER, 0, Uint32Array.from(work))
+               this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, null)
+
+               this.#gl.beginQuery(this.#gl.ANY_SAMPLES_PASSED_CONSERVATIVE, this.#query)
+               this.#gl.drawArrays(this.#gl.TRIANGLES, 0, 6)
+               this.#gl.endQuery(this.#gl.ANY_SAMPLES_PASSED_CONSERVATIVE)
+       }
+
+       static async checkQueryResult (): Promise<boolean> {
+               if (this.#gl == null || this.#query == null) throw new Error('WebGL 2 is required to check query results')
+               if (this.#gl.getQueryParameter(this.#query, this.#gl.QUERY_RESULT_AVAILABLE)) {
+                       return this.#gl.getQueryParameter(this.#query, this.#gl.QUERY_RESULT)
                }
-               function readBackResult () {
-                       if (PowGl.#gl == null) throw new Error('WebGL 2 is required to check read results')
-                       PowGl.#gl.readPixels(0, 0, PowGl.#gl.drawingBufferWidth, PowGl.#gl.drawingBufferHeight, PowGl.#gl.RGBA, PowGl.#gl.UNSIGNED_BYTE, PowGl.#pixels)
-                       // Check the pixels for any success
-                       for (let i = 0; i < PowGl.#pixels.length; i += 4) {
-                               if (PowGl.#pixels[i] !== 0) {
-                                       const hex = PowGl.#hexify(work.subarray(4, 8)) + PowGl.#hexify([
-                                               PowGl.#pixels[i + 2],
-                                               PowGl.#pixels[i + 3],
-                                               work[2] ^ (PowGl.#pixels[i] - 1),
-                                               work[3] ^ (PowGl.#pixels[i + 1] - 1)
-                                       ])
-                                       // Return the work value with the custom bits
-                                       typeof callback === 'function' && callback(hex)
-                                       return
-                               }
+               // Query result not yet available, check again in the next frame
+               return new Promise((resolve, reject): void => {
+                       try {
+                               requestAnimationFrame(async (): Promise<void> => {
+                                       const result = await PowGl.checkQueryResult()
+                                       resolve(result)
+                               })
+                       } catch (err) {
+                               reject(err)
+                       }
+               })
+       }
+
+       /**
+       * Reads pixels into the work buffer, checks every 4th pixel for the 'found'
+       * byte, converts the subsequent 3 pixels with the nonce byte values to a hex
+       * string, and returns the result.
+       *
+       * @param work - Buffer with the original random nonce value
+       * @returns Nonce as an 8-byte (16-char) hexadecimal string
+       */
+       static readResult (work: Uint8Array): string {
+               if (this.#gl == null) throw new Error('WebGL 2 is required to read pixels')
+               this.#gl.readPixels(0, 0, this.#gl.drawingBufferWidth, this.#gl.drawingBufferHeight, this.#gl.RGBA, this.#gl.UNSIGNED_BYTE, this.#pixels)
+               for (let i = 0; i < this.#pixels.length; i += 4) {
+                       if (this.#pixels[i] !== 0) {
+                               // Return the work value with the custom bits
+                               const hex = this.#hexify(work.subarray(4, 8)) + this.#hexify([
+                                       this.#pixels[i + 2],
+                                       this.#pixels[i + 3],
+                                       work[2] ^ (this.#pixels[i] - 1),
+                                       work[3] ^ (this.#pixels[i + 1] - 1)
+                               ])
+                               return hex
                        }
                }
-               draw()
+               throw new Error('Query reported result but nonce value not found')
        }
 }