From: Chris Duncan Date: Sun, 15 Dec 2024 07:30:25 +0000 (-0800) Subject: Reorganize compilation into static initialization block. X-Git-Url: https://zoso.dev/?a=commitdiff_plain;h=c0503e9116a832c15b511fb7d36b697c21221348;p=libnemo.git Reorganize compilation into static initialization block. --- diff --git a/src/lib/workers/pow.ts b/src/lib/workers/pow.ts index 1de78ff..27083b8 100644 --- a/src/lib/workers/pow.ts +++ b/src/lib/workers/pow.ts @@ -2,9 +2,16 @@ // 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 { return new Promise(resolve => { - this.calculate(hash, threshold, resolve) + this.#calculate(hash, resolve, threshold) }) } @@ -14,12 +21,187 @@ export class Pow { * Author: numtel * License: MIT */ + // Vertext Shader + static vsSource = `#version 300 es +precision highp float; +layout (location=0) in vec4 position; +layout (location=1) in vec2 uv; + +out vec2 uv_pos; + +void main() { + uv_pos = uv; + gl_Position = position; +}` + + // Fragment shader + static fsSource = `#version 300 es +precision highp float; +precision highp int; + +in vec2 uv_pos; +out vec4 fragColor; + +// Random work values +// First 2 bytes will be overwritten by texture pixel position +// Second 2 bytes will be modified if the canvas size is greater than 256x256 +uniform uvec4 u_work0; +// Last 4 bytes remain as generated externally +uniform uvec4 u_work1; +// Precalculated block hash components +uniform uint blockHash[8]; +// Threshold is 0xfffffff8 for send/change blocks and 0xfffffe for all else +uniform uint threshold; +// Defines canvas size +uniform float workload; + +// Defined separately from uint v[32] below as the original value is required +// to calculate the second uint32 of the digest for threshold comparison +#define BLAKE2B_IV32_1 0x6A09E667u + +// Both buffers represent 16 uint64s as 32 uint32s +// because that's what GLSL offers, just like Javascript + +// Compression buffer, intialized to 2 instances of the initialization vector +// The following values have been modified from the BLAKE2B_IV: +// OUTLEN is constant 8 bytes +// v[0] ^= 0x01010000u ^ uint(OUTLEN); +// INLEN is constant 40 bytes: work value (8) + block hash (32) +// v[24] ^= uint(INLEN); +// It's always the "last" compression at this INLEN +// v[28] = ~v[28]; +// v[29] = ~v[29]; +uint v[32] = uint[32]( + 0xF2BDC900u, 0x6A09E667u, 0x84CAA73Bu, 0xBB67AE85u, + 0xFE94F82Bu, 0x3C6EF372u, 0x5F1D36F1u, 0xA54FF53Au, + 0xADE682D1u, 0x510E527Fu, 0x2B3E6C1Fu, 0x9B05688Cu, + 0xFB41BD6Bu, 0x1F83D9ABu, 0x137E2179u, 0x5BE0CD19u, + 0xF3BCC908u, 0x6A09E667u, 0x84CAA73Bu, 0xBB67AE85u, + 0xFE94F82Bu, 0x3C6EF372u, 0x5F1D36F1u, 0xA54FF53Au, + 0xADE682F9u, 0x510E527Fu, 0x2B3E6C1Fu, 0x9B05688Cu, + 0x04BE4294u, 0xE07C2654u, 0x137E2179u, 0x5BE0CD19u +); +// Input data buffer +uint m[32]; + +// These are offsets into the input data buffer for each mixing step. +// They are multiplied by 2 from the original SIGMA values in +// the C reference implementation, which refered to uint64s. +const int SIGMA82[192] = int[192]( + 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,4,24,12,20,0,22,16,6,8,26,14,10,30,28,2,18,24,10,2,30,28,26,8,20,0, + 14,12,6,18,4,16,22,26,22,14,28,24,2,6,18,10,0,30,8,16,12,4,20,12,30,28, + 18,22,6,0,16,24,4,26,14,2,8,20,10,20,4,16,8,14,12,2,10,30,22,18,28,6,24, + 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 +); + +// 64-bit unsigned addition within the compression buffer +// Sets v[a,a+1] += b +// b0 is the low 32 bits of b, b1 represents the high 32 bits +void add_uint64 (int a, uint b0, uint b1) { + uint o0 = v[a] + b0; + uint o1 = v[a + 1] + b1; + if (v[a] > 0xFFFFFFFFu - b0) { // did low 32 bits overflow? + o1++; + } + v[a] = o0; + v[a + 1] = o1; +} + +// G Mixing function +void B2B_G (int a, int b, int c, int d, int ix, int iy) { + add_uint64(a, v[b], v[b+1]); + add_uint64(a, m[ix], m[ix + 1]); + + // v[d,d+1] = (v[d,d+1] xor v[a,a+1]) rotated to the right by 32 bits + uint xor0 = v[d] ^ v[a]; + uint xor1 = v[d + 1] ^ v[a + 1]; + v[d] = xor1; + v[d + 1] = xor0; + + add_uint64(c, v[d], v[d+1]); + + // v[b,b+1] = (v[b,b+1] xor v[c,c+1]) rotated right by 24 bits + xor0 = v[b] ^ v[c]; + xor1 = v[b + 1] ^ v[c + 1]; + v[b] = (xor0 >> 24) ^ (xor1 << 8); + v[b + 1] = (xor1 >> 24) ^ (xor0 << 8); + + add_uint64(a, v[b], v[b+1]); + add_uint64(a, m[iy], m[iy + 1]); + + // v[d,d+1] = (v[d,d+1] xor v[a,a+1]) rotated right by 16 bits + xor0 = v[d] ^ v[a]; + xor1 = v[d + 1] ^ v[a + 1]; + v[d] = (xor0 >> 16) ^ (xor1 << 16); + v[d + 1] = (xor1 >> 16) ^ (xor0 << 16); + + add_uint64(c, v[d], v[d+1]); + + // v[b,b+1] = (v[b,b+1] xor v[c,c+1]) rotated right by 63 bits + xor0 = v[b] ^ v[c]; + xor1 = v[b + 1] ^ v[c + 1]; + v[b] = (xor1 >> 31) ^ (xor0 << 1); + v[b + 1] = (xor0 >> 31) ^ (xor1 << 1); +} + +void main() { + int i; + uint uv_x = uint(uv_pos.x * workload); + uint uv_y = uint(uv_pos.y * workload); + uint x_pos = uv_x % 256u; + uint y_pos = uv_y % 256u; + uint x_index = (uv_x - x_pos) / 256u; + uint y_index = (uv_y - y_pos) / 256u; + + // First 2 work bytes are the x,y pos within the 256x256 area, the next + // two bytes are modified from the random generated value, XOR'd with + // the x,y area index of where this pixel is located + m[0] = (x_pos ^ (y_pos << 8) ^ ((u_work0.b ^ x_index) << 16) ^ ((u_work0.a ^ y_index) << 24)); + // Remaining bytes are un-modified from the random generated value + m[1] = (u_work1.r ^ (u_work1.g << 8) ^ (u_work1.b << 16) ^ (u_work1.a << 24)); + + // Block hash + m[2] = blockHash[0]; + m[3] = blockHash[1]; + m[4] = blockHash[2]; + m[5] = blockHash[3]; + m[6] = blockHash[4]; + m[7] = blockHash[5]; + m[8] = blockHash[6]; + m[9] = blockHash[7]; + + // twelve rounds of mixing + for(i=0;i<12;i++) { + B2B_G(0, 8, 16, 24, SIGMA82[i * 16 + 0], SIGMA82[i * 16 + 1]); + B2B_G(2, 10, 18, 26, SIGMA82[i * 16 + 2], SIGMA82[i * 16 + 3]); + B2B_G(4, 12, 20, 28, SIGMA82[i * 16 + 4], SIGMA82[i * 16 + 5]); + B2B_G(6, 14, 22, 30, SIGMA82[i * 16 + 6], SIGMA82[i * 16 + 7]); + B2B_G(0, 10, 20, 30, SIGMA82[i * 16 + 8], SIGMA82[i * 16 + 9]); + B2B_G(2, 12, 22, 24, SIGMA82[i * 16 + 10], SIGMA82[i * 16 + 11]); + B2B_G(4, 14, 16, 26, SIGMA82[i * 16 + 12], SIGMA82[i * 16 + 13]); + B2B_G(6, 8, 18, 28, SIGMA82[i * 16 + 14], SIGMA82[i * 16 + 15]); + } + + // Threshold test, first 4 bytes not significant, + // only calculate digest of the second 4 bytes + if((BLAKE2B_IV32_1 ^ v[1] ^ v[17]) > threshold) { + // Success found, return pixel data so work value can be constructed + fragColor = vec4( + float(x_index + 1u)/255., // +1 to distinguish from 0 (unsuccessful) pixels + float(y_index + 1u)/255., // Same as previous + float(x_pos)/255., // Return the 2 custom bytes used in work value + float(y_pos)/255. // Second custom byte + ); + } +}` static SEND_THRESHOLD = 0xFFFFFFF8 /** Used to set canvas size. Must be a multiple of 256. */ - static WORKLOAD: number = 256 * 4 - static gl = new OffscreenCanvas(this.WORKLOAD, this.WORKLOAD).getContext('webgl2') as WebGL2RenderingContext - static program = this.gl.createProgram() + 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[] = [ @@ -53,181 +235,6 @@ export class Pow { this.B2B_G_params.push(this.SIGMA82[i * 16 + 15]) } } - // Vertext Shader - static vsSource = `#version 300 es - precision highp float; - layout (location=0) in vec4 position; - layout (location=1) in vec2 uv; - - out vec2 uv_pos; - - void main() { - uv_pos = uv; - gl_Position = position; - }` - - // Fragment shader - static fsSource = `#version 300 es - precision highp float; - precision highp int; - - in vec2 uv_pos; - out vec4 fragColor; - - // Random work values - // First 2 bytes will be overwritten by texture pixel position - // Second 2 bytes will be modified if the canvas size is greater than 256x256 - uniform uvec4 u_work0; - // Last 4 bytes remain as generated externally - uniform uvec4 u_work1; - // Precalculated block hash components - uniform uint blockHash[8]; - // Threshold is 0xfffffff8 for send/change blocks and 0xfffffe for all else - uniform uint threshold; - - // Defined separately from uint v[32] below as the original value is required - // to calculate the second uint32 of the digest for threshold comparison - #define BLAKE2B_IV32_1 0x6A09E667u - - // Both buffers represent 16 uint64s as 32 uint32s - // because that's what GLSL offers, just like Javascript - - // Compression buffer, intialized to 2 instances of the initialization vector - // The following values have been modified from the BLAKE2B_IV: - // OUTLEN is constant 8 bytes - // v[0] ^= 0x01010000u ^ uint(OUTLEN); - // INLEN is constant 40 bytes: work value (8) + block hash (32) - // v[24] ^= uint(INLEN); - // It's always the "last" compression at this INLEN - // v[28] = ~v[28]; - // v[29] = ~v[29]; - uint v[32] = uint[32]( - 0xF2BDC900u, 0x6A09E667u, 0x84CAA73Bu, 0xBB67AE85u, - 0xFE94F82Bu, 0x3C6EF372u, 0x5F1D36F1u, 0xA54FF53Au, - 0xADE682D1u, 0x510E527Fu, 0x2B3E6C1Fu, 0x9B05688Cu, - 0xFB41BD6Bu, 0x1F83D9ABu, 0x137E2179u, 0x5BE0CD19u, - 0xF3BCC908u, 0x6A09E667u, 0x84CAA73Bu, 0xBB67AE85u, - 0xFE94F82Bu, 0x3C6EF372u, 0x5F1D36F1u, 0xA54FF53Au, - 0xADE682F9u, 0x510E527Fu, 0x2B3E6C1Fu, 0x9B05688Cu, - 0x04BE4294u, 0xE07C2654u, 0x137E2179u, 0x5BE0CD19u - ); - // Input data buffer - uint m[32]; - - // These are offsets into the input data buffer for each mixing step. - // They are multiplied by 2 from the original SIGMA values in - // the C reference implementation, which refered to uint64s. - const int SIGMA82[192] = int[192]( - 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,4,24,12,20,0,22,16,6,8,26,14,10,30,28,2,18,24,10,2,30,28,26,8,20,0, - 14,12,6,18,4,16,22,26,22,14,28,24,2,6,18,10,0,30,8,16,12,4,20,12,30,28, - 18,22,6,0,16,24,4,26,14,2,8,20,10,20,4,16,8,14,12,2,10,30,22,18,28,6,24, - 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 - ); - - // 64-bit unsigned addition within the compression buffer - // Sets v[a,a+1] += b - // b0 is the low 32 bits of b, b1 represents the high 32 bits - void add_uint64 (int a, uint b0, uint b1) { - uint o0 = v[a] + b0; - uint o1 = v[a + 1] + b1; - if (v[a] > 0xFFFFFFFFu - b0) { // did low 32 bits overflow? - o1++; - } - v[a] = o0; - v[a + 1] = o1; - } - - // G Mixing function - void B2B_G (int a, int b, int c, int d, int ix, int iy) { - add_uint64(a, v[b], v[b+1]); - add_uint64(a, m[ix], m[ix + 1]); - - // v[d,d+1] = (v[d,d+1] xor v[a,a+1]) rotated to the right by 32 bits - uint xor0 = v[d] ^ v[a]; - uint xor1 = v[d + 1] ^ v[a + 1]; - v[d] = xor1; - v[d + 1] = xor0; - - add_uint64(c, v[d], v[d+1]); - - // v[b,b+1] = (v[b,b+1] xor v[c,c+1]) rotated right by 24 bits - xor0 = v[b] ^ v[c]; - xor1 = v[b + 1] ^ v[c + 1]; - v[b] = (xor0 >> 24) ^ (xor1 << 8); - v[b + 1] = (xor1 >> 24) ^ (xor0 << 8); - - add_uint64(a, v[b], v[b+1]); - add_uint64(a, m[iy], m[iy + 1]); - - // v[d,d+1] = (v[d,d+1] xor v[a,a+1]) rotated right by 16 bits - xor0 = v[d] ^ v[a]; - xor1 = v[d + 1] ^ v[a + 1]; - v[d] = (xor0 >> 16) ^ (xor1 << 16); - v[d + 1] = (xor1 >> 16) ^ (xor0 << 16); - - add_uint64(c, v[d], v[d+1]); - - // v[b,b+1] = (v[b,b+1] xor v[c,c+1]) rotated right by 63 bits - xor0 = v[b] ^ v[c]; - xor1 = v[b + 1] ^ v[c + 1]; - v[b] = (xor1 >> 31) ^ (xor0 << 1); - v[b + 1] = (xor0 >> 31) ^ (xor1 << 1); - } - - void main() { - int i; - uint uv_x = uint(uv_pos.x * ${this.WORKLOAD - 1}.); - uint uv_y = uint(uv_pos.y * ${this.WORKLOAD - 1}.); - uint x_pos = uv_x % 256u; - uint y_pos = uv_y % 256u; - uint x_index = (uv_x - x_pos) / 256u; - uint y_index = (uv_y - y_pos) / 256u; - - // First 2 work bytes are the x,y pos within the 256x256 area, the next - // two bytes are modified from the random generated value, XOR'd with - // the x,y area index of where this pixel is located - m[0] = (x_pos ^ (y_pos << 8) ^ ((u_work0.b ^ x_index) << 16) ^ ((u_work0.a ^ y_index) << 24)); - // Remaining bytes are un-modified from the random generated value - m[1] = (u_work1.r ^ (u_work1.g << 8) ^ (u_work1.b << 16) ^ (u_work1.a << 24)); - - // Block hash - m[2] = blockHash[0]; - m[3] = blockHash[1]; - m[4] = blockHash[2]; - m[5] = blockHash[3]; - m[6] = blockHash[4]; - m[7] = blockHash[5]; - m[8] = blockHash[6]; - m[9] = blockHash[7]; - - // twelve rounds of mixing - for(i=0;i<12;i++) { - B2B_G(0, 8, 16, 24, SIGMA82[i * 16 + 0], SIGMA82[i * 16 + 1]); - B2B_G(2, 10, 18, 26, SIGMA82[i * 16 + 2], SIGMA82[i * 16 + 3]); - B2B_G(4, 12, 20, 28, SIGMA82[i * 16 + 4], SIGMA82[i * 16 + 5]); - B2B_G(6, 14, 22, 30, SIGMA82[i * 16 + 6], SIGMA82[i * 16 + 7]); - B2B_G(0, 10, 20, 30, SIGMA82[i * 16 + 8], SIGMA82[i * 16 + 9]); - B2B_G(2, 12, 22, 24, SIGMA82[i * 16 + 10], SIGMA82[i * 16 + 11]); - B2B_G(4, 14, 16, 26, SIGMA82[i * 16 + 12], SIGMA82[i * 16 + 13]); - B2B_G(6, 8, 18, 28, SIGMA82[i * 16 + 14], SIGMA82[i * 16 + 15]); - } - - // Threshold test, first 4 bytes not significant, - // only calculate digest of the second 4 bytes - if((BLAKE2B_IV32_1 ^ v[1] ^ v[17]) > threshold) { - // Success found, return pixel data so work value can be constructed - fragColor = vec4( - float(x_index + 1u)/255., // +1 to distinguish from 0 (unsuccessful) pixels - float(y_index + 1u)/255., // Same as previous - float(x_pos)/255., // Return the 2 custom bytes used in work value - float(y_pos)/255. // Second custom byte - ); - } - }` static hexify (arr: number[] | Uint8Array): string { @@ -251,83 +258,88 @@ export class Pow { } /* - for (i = 0; i<12; i++) { - B2B_G(0, 8, 16, 24, SIGMA82[i * 16 + 0], SIGMA82[i * 16 + 1]); - -> - B2B_G(0, 8, 16, 24, SIGMA82[i * 16 + 0], SIGMA82[i * 16 + 1]); + for (i = 0; i<12; i++) { + B2B_G(0, 8, 16, 24, SIGMA82[i * 16 + 0], SIGMA82[i * 16 + 1]); + -> + 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 + // Vertex Positions, 2 triangles + 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([ + 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1 + ]) + + // Compile static { - - const vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER) - if (vertexShader == null) - throw 'error creating vertex shader' - this.gl.shaderSource(vertexShader, this.vsSource) - this.gl.compileShader(vertexShader) - - if (!this.gl.getShaderParameter(vertexShader, this.gl.COMPILE_STATUS)) - throw this.gl.getShaderInfoLog(vertexShader) - - const fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER) - if (fragmentShader == null) - throw 'error creating fragment shader' - this.gl.shaderSource(fragmentShader, this.fsSource) - this.gl.compileShader(fragmentShader) - - if (!this.gl.getShaderParameter(fragmentShader, this.gl.COMPILE_STATUS)) - throw this.gl.getShaderInfoLog(fragmentShader) - - this.gl.attachShader(this.program, vertexShader) - this.gl.attachShader(this.program, fragmentShader) + 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 this.gl.getProgramInfoLog(this.program) - - this.gl.useProgram(this.program) + 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) - // 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 = this.gl.createBuffer() - this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer) - this.gl.bufferData(this.gl.ARRAY_BUFFER, positions, this.gl.STATIC_DRAW) + 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) - // Texture Positions - const uvPosArray = new Float32Array([ - 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1 - ]) const uvBuffer = this.gl.createBuffer() this.gl.bindBuffer(this.gl.ARRAY_BUFFER, uvBuffer) - this.gl.bufferData(this.gl.ARRAY_BUFFER, uvPosArray, this.gl.STATIC_DRAW) + 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) - } - static work0Location = this.gl.getUniformLocation(this.program, 'u_work0') - static work1Location = this.gl.getUniformLocation(this.program, 'u_work1') - static blockHashLocation = this.gl.getUniformLocation(this.program, "blockHash") - static thresholdLocation = this.gl.getUniformLocation(this.program, "threshold") + 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") + } - /** - * self.NanoWebglPow (hashHex, threshold, callback) - * @param {string} hashHex - Previous Block Hash as Hex String - * @param {string} threshold - Optional difficulty threshold(default=0xFFFFFFF8 since v21) - * @param {function} callback - Called when work value found - * Receives single string argument, work value as hex - */ - static calculate (hashHex: string, threshold: number = this.SEND_THRESHOLD, callback: (nonce: string | PromiseLike) => any): void { - if (typeof threshold !== 'number') throw new TypeError(`invalid_threshold ${threshold}`) - if (!/^[A-F-a-f0-9]{64}$/.test(hashHex)) throw new Error(`invalid_hash ${hashHex}`) - if (this.gl == null) throw new Error('webgl2_required') + static #calculate (hashHex: string, callback: (nonce: string | PromiseLike) => any, threshold: number = this.SEND_THRESHOLD): 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) @@ -345,11 +357,12 @@ export class Pow { 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) - this.gl.clear(this.gl.COLOR_BUFFER_BIT) - this.gl.drawArrays(this.gl.TRIANGLES, 0, 6) - const pixels = new Uint8Array(this.gl.drawingBufferWidth * this.gl.drawingBufferHeight * 4) - this.gl.readPixels(0, 0, this.gl.drawingBufferWidth, this.gl.drawingBufferHeight, this.gl.RGBA, this.gl.UNSIGNED_BYTE, pixels) + 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) {