// SPDX-License-Identifier: GPL-3.0-or-later
//@ts-nocheck
import { Blake2b } from './blake2b.js'
-// nano-webgl-pow
-// Nano Currency Proof of Work Value generation using WebGL2
-// Author: numtel <ben@latenightsketches.com>
-// License: MIT
-
-// window.NanoWebglPow(hashHex, callback, progressCallback, threshold);
-// @param hashHex String Previous Block Hash as Hex String
-// @param callback Function Called when work value found
-// Receives single string argument, work value as hex
-// @param progressCallback Function Optional
-// Receives single argument: n, number of frames so far
-// Return true to abort
-// @param threshold Number|String Optional difficulty threshold (default=0xFFFFFFF8 since v21)
-
-(function () {
-
- function array_hex (arr, index, length) {
- let out = ''
- for (let i = length - 1; i > -1; i--) {
- out += (arr[i] > 15 ? '' : '0') + arr[i].toString(16)
- }
- return out
- }
-
- function hex_reverse (hex) {
- let out = ''
- for (let i = hex.length; i > 0; i -= 2) {
- out += hex.slice(i - 2, i)
- }
- return out
- }
-
- function calculate (hashHex, callback, progressCallback, threshold = '0xFFFFFFF8') {
- if (typeof threshold === 'number') threshold = '0x' + threshold.toString(16)
-
- const canvas = document.createElement('canvas')
-
- canvas.width = window.NanoWebglPow.width
- canvas.height = window.NanoWebglPow.height
-
- const gl = canvas.getContext('webgl2')
-
- if (!gl)
- throw new Error('webgl2_required')
-
- if (!/^[A-F-a-f0-9]{64}$/.test(hashHex))
- throw new Error('invalid_hash')
-
- gl.clearColor(0, 0, 0, 1)
-
- const reverseHex = hex_reverse(hashHex)
- // Vertext Shader
- const vsSource = `#version 300 es
+const p = () => {
+ const NONCE_BYTES = 8
+ const RECEIVE_THRESHOLD = '0xfffffe'
+ const SEND_THRESHOLD = '0xfffffff8'
+
+ /**
+ * Listens for messages from a calling function.
+ */
+ addEventListener('message', (message) => {
+ const data = JSON.parse(new TextDecoder().decode(message.data ?? message))
+ for (const d of data) {
+ if (d === 'stop') {
+ close()
+ postMessage(new ArrayBuffer(0))
+ } else {
+ find(d.hash, d.threshold ?? SEND_THRESHOLD).then(nonce => {
+ console.log(`pow found: ${nonce}`)
+ d.work = nonce
+ const buf = new TextEncoder().encode(JSON.stringify(data)).buffer
+ //@ts-expect-error
+ postMessage(buf, [buf])
+ })
+ }
+ }
+ })
+
+ async function find (hash: string, threshold: string = SEND_THRESHOLD): Promise<void> {
+ console.log(`hash: ${hash}`)
+ return new Promise(resolve => {
+ calculate(hash, resolve, console.log, threshold)
+ })
+ // let result = null
+ // let count = 0
+ // do {
+ // count++
+ // const nonce: Uint8Array = new Uint8Array(NONCE_BYTES)
+ // crypto.getRandomValues(nonce)
+ // const test: string = new Blake2b(NONCE_BYTES)
+ // .update(nonce)
+ // .update(parseHex(hash))
+ // .digest('hex') as string
+ // if (count % 1000 === 0) console.log(`${count} hashes...`)
+ // if (BigInt(`0x${test}`) >= BigInt(`0x${threshold}`)) {
+ // result = nonce
+ // }
+ // } while (result == null)
+ // return result
+ }
+
+ /**
+ * nano-webgl-pow
+ * Nano Currency Proof of Work Value generation using WebGL2
+ * Author: numtel <ben@latenightsketches.com>
+ * License: MIT
+
+ * self.NanoWebglPow(hashHex, callback, progressCallback, threshold);
+ * @param hashHex String Previous Block Hash as Hex String
+ * @param callback Function Called when work value found
+ * Receives single string argument, work value as hex
+ * @param progressCallback Function Optional
+ * Receives single argument: n, number of frames so far
+ * Return true to abort
+ * @param threshold Number|String Optional difficulty threshold (default=0xFFFFFFF8 since v21)
+ */
+
+ // Both width and height must be multiple of 256, (one byte)
+ // but do not need to be the same,
+ // matching GPU capabilities is the aim
+ const webglWidth = 256 * 2
+ const webglHeight = 256 * 2
+
+ function hexify (arr: number[] | Uint8Array): string {
+ let out = ''
+ for (let i = arr.length - 1; i >= 0; i--) {
+ out += arr[i].toString(16).padStart(2, '0')
+ }
+ return out
+ }
+
+ function hex_reverse (hex: string): string {
+ let out = ''
+ for (let i = hex.length; i > 0; i -= 2) {
+ out += hex.slice(i - 2, i)
+ }
+ return out
+ }
+
+ function calculate (hashHex, callback, progressCallback, threshold = '0xFFFFFFF8') {
+ if (typeof threshold === 'number') threshold = '0x' + threshold.toString(16)
+
+ const canvas = new OffscreenCanvas(webglWidth, webglHeight)
+ const gl = canvas.getContext('webgl2')
+
+ if (!gl)
+ throw new Error('webgl2_required')
+
+ if (!/^[A-F-a-f0-9]{64}$/.test(hashHex))
+ throw new Error(`invalid_hash ${hashHex}`)
+
+ gl.clearColor(0, 0, 0, 1)
+
+ const reverseHex = hex_reverse(hashHex)
+
+ // Vertext Shader
+ const vsSource = `#version 300 es
precision highp float;
layout (location=0) in vec4 position;
layout (location=1) in vec2 uv;
gl_Position = position;
}`
- // Fragment shader
- const fsSource = `#version 300 es
+ // Fragment shader
+ const fsSource = `#version 300 es
precision highp float;
precision highp int;
}
}`
- const vertexShader = gl.createShader(gl.VERTEX_SHADER)
- gl.shaderSource(vertexShader, vsSource)
- gl.compileShader(vertexShader)
-
- if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS))
- throw gl.getShaderInfoLog(vertexShader)
-
- const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
- gl.shaderSource(fragmentShader, fsSource)
- gl.compileShader(fragmentShader)
-
- if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS))
- throw gl.getShaderInfoLog(fragmentShader)
-
- const program = gl.createProgram()
- gl.attachShader(program, vertexShader)
- gl.attachShader(program, fragmentShader)
- gl.linkProgram(program)
-
- if (!gl.getProgramParameter(program, gl.LINK_STATUS))
- throw gl.getProgramInfoLog(program)
-
- gl.useProgram(program)
-
- // Construct simple 2D geometry
- const triangleArray = gl.createVertexArray()
- gl.bindVertexArray(triangleArray)
-
- // Vertex Positions, 2 triangles
- const positions = new Float32Array([
- -1, -1, 0, -1, 1, 0, 1, 1, 0,
- 1, -1, 0, 1, 1, 0, -1, -1, 0
- ])
- const positionBuffer = gl.createBuffer()
- gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
- gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW)
- gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0)
- gl.enableVertexAttribArray(0)
-
- // Texture Positions
- const uvPosArray = new Float32Array([
- 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1
- ])
- const uvBuffer = gl.createBuffer()
- gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer)
- gl.bufferData(gl.ARRAY_BUFFER, uvPosArray, gl.STATIC_DRAW)
- gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, 0)
- gl.enableVertexAttribArray(1)
-
- const work0Location = gl.getUniformLocation(program, 'u_work0')
- const work1Location = gl.getUniformLocation(program, 'u_work1')
-
- // Draw output until success or progressCallback says to stop
- const work0 = new Uint8Array(4)
- const work1 = new Uint8Array(4)
- let n = 0
-
- function draw () {
- n++
- window.crypto.getRandomValues(work0)
- window.crypto.getRandomValues(work1)
-
- gl.uniform4uiv(work0Location, Array.from(work0))
- gl.uniform4uiv(work1Location, Array.from(work1))
-
- // Check with progressCallback every 100 frames
- if (n % 100 === 0 && typeof progressCallback === 'function' && progressCallback(n))
- return
-
- gl.clear(gl.COLOR_BUFFER_BIT)
- gl.drawArrays(gl.TRIANGLES, 0, 6)
- const pixels = new Uint8Array(gl.drawingBufferWidth * gl.drawingBufferHeight * 4)
- gl.readPixels(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels)
-
- // Check the pixels for any success
- for (let i = 0; i < pixels.length; i += 4) {
- if (pixels[i] !== 0) {
- // Return the work value with the custom bits
- typeof callback === 'function' &&
- callback(
- array_hex(work1, 0, 4) +
- array_hex([
- pixels[i + 2],
- pixels[i + 3],
- work0[2] ^ (pixels[i] - 1),
- work0[3] ^ (pixels[i + 1] - 1)
- ], 0, 4), n)
- return
- }
- }
- // Nothing found yet, try again
- window.requestAnimationFrame(draw)
- }
-
- // Begin generation
- window.requestAnimationFrame(draw)
- }
-
- window.NanoWebglPow = calculate
- // Both width and height must be multiple of 256, (one byte)
- // but do not need to be the same,
- // matching GPU capabilities is the aim
- window.NanoWebglPow.width = 256 * 2
- window.NanoWebglPow.height = 256 * 2
-
-})()
-
-
-const p = () => {
- const NONCE_BYTES = 8
- const RECEIVE_THRESHOLD = 'fffffe0000000000'
- const SEND_THRESHOLD = 'fffffff800000000'
-
- /**
- * Listens for messages from a calling function.
- */
- addEventListener('message', (message) => {
- const data = JSON.parse(new TextDecoder().decode(message.data ?? message))
- if (data === 'stop') close()
- for (const d of data) {
- find(d.hash, d.threshold).then(nonce => {
- console.log('pow found')
- d.work = nonce
- const buf = new TextEncoder().encode(JSON.stringify(data)).buffer
- //@ts-expect-error
- postMessage(buf, [buf])
- })
- }
- })
-
- async function find (hash: string, threshold: string = SEND_THRESHOLD) {
- return new Promise((resolve, reject) => {
- window.NanoWebglPow(hash, resolve, console.log)
-
- })
- // let result = null
- // let count = 0
- // do {
- // count++
- // const nonce: Uint8Array = new Uint8Array(NONCE_BYTES)
- // crypto.getRandomValues(nonce)
- // const test: string = new Blake2b(NONCE_BYTES)
- // .update(nonce)
- // .update(parseHex(hash))
- // .digest('hex') as string
- // if (count % 1000 === 0) console.log(`${count} hashes...`)
- // if (BigInt(`0x${test}`) >= BigInt(`0x${threshold}`)) {
- // result = nonce
- // }
- // } while (result == null)
- // return result
- }
-
- function parseHex (hex: string) {
- if (hex.length % 2 === 1) hex = `0${hex}`
- const arr = hex.match(/.{1,2}/g)?.map(byte => parseInt(byte, 16))
- return Uint8Array.from(arr ?? [])
- }
-
- return { find }
+ const vertexShader = gl.createShader(gl.VERTEX_SHADER)
+ gl.shaderSource(vertexShader, vsSource)
+ gl.compileShader(vertexShader)
+
+ if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS))
+ throw gl.getShaderInfoLog(vertexShader)
+
+ const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
+ gl.shaderSource(fragmentShader, fsSource)
+ gl.compileShader(fragmentShader)
+
+ if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS))
+ throw gl.getShaderInfoLog(fragmentShader)
+
+ const program = gl.createProgram()
+ gl.attachShader(program, vertexShader)
+ gl.attachShader(program, fragmentShader)
+ gl.linkProgram(program)
+
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS))
+ throw gl.getProgramInfoLog(program)
+
+ gl.useProgram(program)
+
+ // Construct simple 2D geometry
+ const triangleArray = gl.createVertexArray()
+ gl.bindVertexArray(triangleArray)
+
+ // Vertex Positions, 2 triangles
+ const positions = new Float32Array([
+ -1, -1, 0, -1, 1, 0, 1, 1, 0,
+ 1, -1, 0, 1, 1, 0, -1, -1, 0
+ ])
+ const positionBuffer = gl.createBuffer()
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
+ gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW)
+ gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0)
+ gl.enableVertexAttribArray(0)
+
+ // Texture Positions
+ const uvPosArray = new Float32Array([
+ 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1
+ ])
+ const uvBuffer = gl.createBuffer()
+ gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer)
+ gl.bufferData(gl.ARRAY_BUFFER, uvPosArray, gl.STATIC_DRAW)
+ gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, 0)
+ gl.enableVertexAttribArray(1)
+
+ const work0Location = gl.getUniformLocation(program, 'u_work0')
+ const work1Location = gl.getUniformLocation(program, 'u_work1')
+
+ // Draw output until success or progressCallback says to stop
+ const work0 = new Uint8Array(4)
+ const work1 = new Uint8Array(4)
+ let n = 0
+
+ function draw () {
+ n++
+ crypto.getRandomValues(work0)
+ crypto.getRandomValues(work1)
+
+ gl.uniform4uiv(work0Location, Array.from(work0))
+ gl.uniform4uiv(work1Location, Array.from(work1))
+
+ // Check with progressCallback every 100 frames
+ if (n % 100 === 0 && typeof progressCallback === 'function' && progressCallback(n))
+ return
+
+ gl.clear(gl.COLOR_BUFFER_BIT)
+ gl.drawArrays(gl.TRIANGLES, 0, 6)
+ const pixels = new Uint8Array(gl.drawingBufferWidth * gl.drawingBufferHeight * 4)
+ gl.readPixels(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels)
+
+ // Check the pixels for any success
+ for (let i = 0; i < pixels.length; i += 4) {
+ if (pixels[i] !== 0) {
+ // Return the work value with the custom bits
+ typeof callback === 'function' &&
+ callback(
+ hexify(work1) +
+ hexify([
+ pixels[i + 2],
+ pixels[i + 3],
+ work0[2] ^ (pixels[i] - 1),
+ work0[3] ^ (pixels[i + 1] - 1)
+ ]), n)
+ return
+ }
+ }
+ // Nothing found yet, try again
+ self.requestAnimationFrame(draw)
+ }
+
+ // Begin generation
+ self.requestAnimationFrame(draw)
+ }
+
+ return { find }
}
export const Pow = p()