// SPDX-License-Identifier: GPL-3.0-or-later
export class Pow {
-
/**
* Finds a nonce that satisfies the Nano proof-of-work requirements.
*
* @param {string} hashHex - 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 = this.SEND_THRESHOLD): Promise<string> {
+ static async find (hash: string, threshold: number = 0xfffffff8): Promise<string> {
return new Promise<string>(resolve => {
this.#calculate(hash, resolve, threshold)
})
* License: MIT
*/
// Vertext Shader
- static vsSource = `#version 300 es
+ static #vsSource = `#version 300 es
precision highp float;
layout (location=0) in vec4 position;
layout (location=1) in vec2 uv;
}`
// Fragment shader
- static fsSource = `#version 300 es
+ static #fsSource = `#version 300 es
precision highp float;
precision highp int;
}
}`
- static SEND_THRESHOLD = 0xFFFFFFF8
/** Used to set canvas size. Must be a multiple of 256. */
- static WORKLOAD: number = 256 * Math.max(1, Math.floor(navigator.hardwareConcurrency / 2))
- static work0 = new Uint8Array(4)
- static work1 = new Uint8Array(4)
- static SIGMA82: number[] = [
+ static #WORKLOAD: number = 256 * Math.max(1, Math.floor(navigator.hardwareConcurrency / 2))
+ static #work0 = new Uint8Array(4)
+ static #work1 = new Uint8Array(4)
+ static #SIGMA82: number[] = [
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 28, 20, 8, 16, 18, 30, 26, 12, 2, 24,
0, 4, 22, 14, 10, 6, 22, 16, 24, 0, 10, 4, 30, 26, 20, 28, 6, 12, 14, 2, 18, 8, 14, 18, 6, 2, 26,
24, 22, 28, 4, 12, 10, 20, 8, 0, 30, 16, 18, 0, 10, 14, 4, 8, 20, 30, 28, 2, 22, 24, 12, 16, 6,
26, 0, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 28, 20, 8, 16, 18, 30, 26, 12,
2, 24, 0, 4, 22, 14, 10, 6
]
- static B2B_G_params: number[] = []
+ static #B2B_G_params: number[] = []
static {
for (let i = 0; i < 12; i++) {
- this.B2B_G_params.push(this.SIGMA82[i * 16 + 0])
- this.B2B_G_params.push(this.SIGMA82[i * 16 + 1])
- this.B2B_G_params.push(this.SIGMA82[i * 16 + 2])
- this.B2B_G_params.push(this.SIGMA82[i * 16 + 3])
- this.B2B_G_params.push(this.SIGMA82[i * 16 + 4])
- this.B2B_G_params.push(this.SIGMA82[i * 16 + 5])
- this.B2B_G_params.push(this.SIGMA82[i * 16 + 6])
- this.B2B_G_params.push(this.SIGMA82[i * 16 + 7])
- this.B2B_G_params.push(this.SIGMA82[i * 16 + 8])
- this.B2B_G_params.push(this.SIGMA82[i * 16 + 9])
- this.B2B_G_params.push(this.SIGMA82[i * 16 + 10])
- this.B2B_G_params.push(this.SIGMA82[i * 16 + 11])
- this.B2B_G_params.push(this.SIGMA82[i * 16 + 12])
- this.B2B_G_params.push(this.SIGMA82[i * 16 + 13])
- this.B2B_G_params.push(this.SIGMA82[i * 16 + 14])
- this.B2B_G_params.push(this.SIGMA82[i * 16 + 15])
+ this.#B2B_G_params.push(this.#SIGMA82[i * 16 + 0])
+ this.#B2B_G_params.push(this.#SIGMA82[i * 16 + 1])
+ this.#B2B_G_params.push(this.#SIGMA82[i * 16 + 2])
+ this.#B2B_G_params.push(this.#SIGMA82[i * 16 + 3])
+ this.#B2B_G_params.push(this.#SIGMA82[i * 16 + 4])
+ this.#B2B_G_params.push(this.#SIGMA82[i * 16 + 5])
+ this.#B2B_G_params.push(this.#SIGMA82[i * 16 + 6])
+ this.#B2B_G_params.push(this.#SIGMA82[i * 16 + 7])
+ this.#B2B_G_params.push(this.#SIGMA82[i * 16 + 8])
+ this.#B2B_G_params.push(this.#SIGMA82[i * 16 + 9])
+ this.#B2B_G_params.push(this.#SIGMA82[i * 16 + 10])
+ this.#B2B_G_params.push(this.#SIGMA82[i * 16 + 11])
+ this.#B2B_G_params.push(this.#SIGMA82[i * 16 + 12])
+ this.#B2B_G_params.push(this.#SIGMA82[i * 16 + 13])
+ this.#B2B_G_params.push(this.#SIGMA82[i * 16 + 14])
+ this.#B2B_G_params.push(this.#SIGMA82[i * 16 + 15])
}
}
-
- static hexify (arr: number[] | Uint8Array): string {
+ static #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
}
- static processHash (hashHex: string): Uint32Array {
+ static #processHash (hashHex: string): Uint32Array {
if (!/^[A-F-a-f0-9]{64}$/.test(hashHex)) {
throw new Error(`invalid_hash ${hashHex}`)
}
B2B_G(0, 8, 16, 24, SIGMA82[i * 16 + 0], SIGMA82[i * 16 + 1]);
*/
- static gl: WebGL2RenderingContext | null
- static program: WebGLProgram | null
- static work0Location: WebGLUniformLocation | null
- static work1Location: WebGLUniformLocation | null
- static blockHashLocation: WebGLUniformLocation | null
- static thresholdLocation: WebGLUniformLocation | null
- static workloadLocation: WebGLUniformLocation | null
- static vertexShader: WebGLShader | null
- static fragmentShader: WebGLShader | null
- static positionBuffer: WebGLBuffer | null
- static uvBuffer: WebGLBuffer | null
+ static #gl: WebGL2RenderingContext | null
+ static #program: WebGLProgram | null
+ static #work0Location: WebGLUniformLocation | null
+ static #work1Location: WebGLUniformLocation | null
+ static #blockHashLocation: WebGLUniformLocation | null
+ static #thresholdLocation: WebGLUniformLocation | null
+ static #workloadLocation: WebGLUniformLocation | null
+ static #vertexShader: WebGLShader | null
+ static #fragmentShader: WebGLShader | null
+ static #positionBuffer: WebGLBuffer | null
+ static #uvBuffer: WebGLBuffer | null
// Vertex Positions, 2 triangles
- static positions = new Float32Array([
+ static #positions = new Float32Array([
-1, -1, 0, -1, 1, 0, 1, 1, 0,
1, -1, 0, 1, 1, 0, -1, -1, 0
])
// Texture Positions
- static uvPosArray = new Float32Array([
+ static #uvPosArray = new Float32Array([
1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1
])
// Compile
static {
- this.gl = new OffscreenCanvas(this.WORKLOAD, this.WORKLOAD).getContext('webgl2')
- if (this.gl == null) throw new Error('WebGL 2 is required')
-
- this.program = this.gl.createProgram()
- if (this.program == null) throw new Error('Failed to create shader program')
-
- this.vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER)
- if (this.vertexShader == null) throw new Error('Failed to create vertex shader')
- this.gl.shaderSource(this.vertexShader, this.vsSource)
- this.gl.compileShader(this.vertexShader)
- if (!this.gl.getShaderParameter(this.vertexShader, this.gl.COMPILE_STATUS))
- throw new Error(this.gl.getShaderInfoLog(this.vertexShader) ?? `Failed to compile vertex shader`)
-
- this.fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER)
- if (this.fragmentShader == null) throw new Error('Failed to create fragment shader')
- this.gl.shaderSource(this.fragmentShader, this.fsSource)
- this.gl.compileShader(this.fragmentShader)
- if (!this.gl.getShaderParameter(this.fragmentShader, this.gl.COMPILE_STATUS))
- throw new Error(this.gl.getShaderInfoLog(this.fragmentShader) ?? `Failed to compile fragment shader`)
-
- this.gl.attachShader(this.program, this.vertexShader)
- this.gl.attachShader(this.program, this.fragmentShader)
- this.gl.linkProgram(this.program)
- if (!this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS))
- throw new Error(this.gl.getProgramInfoLog(this.program) ?? `Failed to link program`)
+ this.#gl = new OffscreenCanvas(this.#WORKLOAD, this.#WORKLOAD).getContext('webgl2')
+ if (this.#gl == null) throw new Error('WebGL 2 is required')
+
+ this.#program = this.#gl.createProgram()
+ if (this.#program == null) throw new Error('Failed to create shader program')
+
+ this.#vertexShader = this.#gl.createShader(this.#gl.VERTEX_SHADER)
+ if (this.#vertexShader == null) throw new Error('Failed to create vertex shader')
+ this.#gl.shaderSource(this.#vertexShader, this.#vsSource)
+ this.#gl.compileShader(this.#vertexShader)
+ if (!this.#gl.getShaderParameter(this.#vertexShader, this.#gl.COMPILE_STATUS))
+ throw new Error(this.#gl.getShaderInfoLog(this.#vertexShader) ?? `Failed to compile vertex shader`)
+
+ this.#fragmentShader = this.#gl.createShader(this.#gl.FRAGMENT_SHADER)
+ if (this.#fragmentShader == null) throw new Error('Failed to create fragment shader')
+ this.#gl.shaderSource(this.#fragmentShader, this.#fsSource)
+ this.#gl.compileShader(this.#fragmentShader)
+ if (!this.#gl.getShaderParameter(this.#fragmentShader, this.#gl.COMPILE_STATUS))
+ throw new Error(this.#gl.getShaderInfoLog(this.#fragmentShader) ?? `Failed to compile fragment shader`)
+
+ this.#gl.attachShader(this.#program, this.#vertexShader)
+ this.#gl.attachShader(this.#program, this.#fragmentShader)
+ this.#gl.linkProgram(this.#program)
+ if (!this.#gl.getProgramParameter(this.#program, this.#gl.LINK_STATUS))
+ throw new Error(this.#gl.getProgramInfoLog(this.#program) ?? `Failed to link program`)
// Construct simple 2D geometry
- this.gl.useProgram(this.program)
- const triangleArray = this.gl.createVertexArray()
- this.gl.bindVertexArray(triangleArray)
-
- this.positionBuffer = this.gl.createBuffer()
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer)
- this.gl.bufferData(this.gl.ARRAY_BUFFER, this.positions, this.gl.STATIC_DRAW)
- this.gl.vertexAttribPointer(0, 3, this.gl.FLOAT, false, 0, 0)
- this.gl.enableVertexAttribArray(0)
-
- const uvBuffer = this.gl.createBuffer()
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, uvBuffer)
- this.gl.bufferData(this.gl.ARRAY_BUFFER, this.uvPosArray, this.gl.STATIC_DRAW)
- this.gl.vertexAttribPointer(1, 2, this.gl.FLOAT, false, 0, 0)
- this.gl.enableVertexAttribArray(1)
-
- this.work0Location = this.gl.getUniformLocation(this.program, 'u_work0')
- this.work1Location = this.gl.getUniformLocation(this.program, 'u_work1')
- this.blockHashLocation = this.gl.getUniformLocation(this.program, "blockHash")
- this.thresholdLocation = this.gl.getUniformLocation(this.program, "threshold")
- this.workloadLocation = this.gl.getUniformLocation(this.program, "workload")
+ this.#gl.useProgram(this.#program)
+ const triangleArray = this.#gl.createVertexArray()
+ this.#gl.bindVertexArray(triangleArray)
+
+ this.#positionBuffer = this.#gl.createBuffer()
+ this.#gl.bindBuffer(this.#gl.ARRAY_BUFFER, this.#positionBuffer)
+ this.#gl.bufferData(this.#gl.ARRAY_BUFFER, this.#positions, this.#gl.STATIC_DRAW)
+ this.#gl.vertexAttribPointer(0, 3, this.#gl.FLOAT, false, 0, 0)
+ this.#gl.enableVertexAttribArray(0)
+
+ this.#uvBuffer = this.#gl.createBuffer()
+ this.#gl.bindBuffer(this.#gl.ARRAY_BUFFER, this.#uvBuffer)
+ this.#gl.bufferData(this.#gl.ARRAY_BUFFER, this.#uvPosArray, this.#gl.STATIC_DRAW)
+ this.#gl.vertexAttribPointer(1, 2, this.#gl.FLOAT, false, 0, 0)
+ this.#gl.enableVertexAttribArray(1)
+
+ this.#work0Location = this.#gl.getUniformLocation(this.#program, 'u_work0')
+ this.#work1Location = this.#gl.getUniformLocation(this.#program, 'u_work1')
+ this.#blockHashLocation = this.#gl.getUniformLocation(this.#program, "blockHash")
+ this.#thresholdLocation = this.#gl.getUniformLocation(this.#program, "threshold")
+ this.#workloadLocation = this.#gl.getUniformLocation(this.#program, "workload")
}
- static #calculate (hashHex: string, callback: (nonce: string | PromiseLike<string>) => any, threshold: number = this.SEND_THRESHOLD): void {
+ static #calculate (hashHex: string, callback: (nonce: string | PromiseLike<string>) => any, threshold: number): void {
if (typeof threshold !== 'number') throw new TypeError(`Invalid threshold ${threshold}`)
- if (this.gl == null) throw new Error('WebGL 2 is required')
- this.gl.clearColor(0, 0, 0, 1)
- const hashBytes: Uint32Array = this.processHash(hashHex)
+ if (this.#gl == null) throw new Error('WebGL 2 is required')
+ this.#gl.clearColor(0, 0, 0, 1)
+ const hashBytes: Uint32Array = this.#processHash(hashHex)
// Draw output until success or progressCallback says to stop
let n = 0
const frameTimes: number[] = []
const draw = (): void => {
n++
- if (Pow.gl == null) throw new Error('webgl2_required')
+ if (Pow.#gl == null) throw new Error('WebGL 2 is required')
performance.mark('start')
- crypto.getRandomValues(Pow.work0)
- crypto.getRandomValues(Pow.work1)
-
- Pow.gl.uniform4uiv(Pow.work0Location, this.work0)
- Pow.gl.uniform4uiv(Pow.work1Location, this.work1)
- Pow.gl.uniform1uiv(Pow.blockHashLocation, hashBytes)
- Pow.gl.uniform1ui(Pow.thresholdLocation, threshold)
- Pow.gl.uniform1f(Pow.workloadLocation, this.WORKLOAD - 1)
-
- Pow.gl.clear(Pow.gl.COLOR_BUFFER_BIT)
- Pow.gl.drawArrays(Pow.gl.TRIANGLES, 0, 6)
- const pixels = new Uint8Array(Pow.gl.drawingBufferWidth * Pow.gl.drawingBufferHeight * 4)
- Pow.gl.readPixels(0, 0, Pow.gl.drawingBufferWidth, Pow.gl.drawingBufferHeight, Pow.gl.RGBA, Pow.gl.UNSIGNED_BYTE, pixels)
+ crypto.getRandomValues(Pow.#work0)
+ crypto.getRandomValues(Pow.#work1)
+
+ Pow.#gl.uniform4uiv(Pow.#work0Location, this.#work0)
+ Pow.#gl.uniform4uiv(Pow.#work1Location, this.#work1)
+ Pow.#gl.uniform1uiv(Pow.#blockHashLocation, hashBytes)
+ Pow.#gl.uniform1ui(Pow.#thresholdLocation, threshold)
+ Pow.#gl.uniform1f(Pow.#workloadLocation, this.#WORKLOAD - 1)
+
+ Pow.#gl.clear(Pow.#gl.COLOR_BUFFER_BIT)
+ Pow.#gl.drawArrays(Pow.#gl.TRIANGLES, 0, 6)
+ const pixels = new Uint8Array(Pow.#gl.drawingBufferWidth * Pow.#gl.drawingBufferHeight * 4)
+ Pow.#gl.readPixels(0, 0, Pow.#gl.drawingBufferWidth, Pow.#gl.drawingBufferHeight, Pow.#gl.RGBA, Pow.#gl.UNSIGNED_BYTE, pixels)
// Check the pixels for any success
for (let i = 0; i < pixels.length; i += 4) {
if (pixels[i] !== 0) {
performance.clearMarks()
console.log(`average frame time: ${(frameTimes.reduce((a, b) => a + b)) / frameTimes.length} ms`)
console.log(`frames calculated: ${n}`)
- const hex = this.hexify(this.work1) + this.hexify([
+ const hex = this.#hexify(this.#work1) + this.#hexify([
pixels[i + 2],
pixels[i + 3],
- this.work0[2] ^ (pixels[i] - 1),
- this.work0[3] ^ (pixels[i + 1] - 1)
+ this.#work0[2] ^ (pixels[i] - 1),
+ this.#work0[3] ^ (pixels[i + 1] - 1)
])
// Return the work value with the custom bits
typeof callback === 'function' && callback(hex)