From f806a9b9c2895ca9b507266621b5c0eea0dde652 Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Sat, 8 Mar 2025 22:22:37 -0800 Subject: [PATCH] Remove bundle from repo. --- main.min.js | 963 ---------------------------------------------------- 1 file changed, 963 deletions(-) delete mode 100644 main.min.js diff --git a/main.min.js b/main.min.js deleted file mode 100644 index f6091be..0000000 --- a/main.min.js +++ /dev/null @@ -1,963 +0,0 @@ -// src/shaders/compute.wgsl -var compute_default = "struct UBO{blockhash:array,2>,seed:vec2,threshold:u32};@group(0)@binding(0)var ubo:UBO;struct WORK{nonce:vec2,found:atomic};@group(0)@binding(1)varwork:WORK;const BLAKE2B_IV_0=vec2(0xF2BDC900u,0x6A09E667u);const Z=vec2(0u);const CARRY=vec4(1u,0u,1u,0u);const ROTATE_1=vec4(1u);const ROTATE_8=vec4(8u);const ROTATE_16=vec4(16u);const ROTATE_24=vec4(24u);const ROTATE_31=vec4(31u);var found:bool;@compute @workgroup_size(32)fn search(@builtin(global_invocation_id)global_id:vec3,@builtin(local_invocation_id)local_id:vec3){found=(local_id.x==0u&&atomicLoad(&work.found)!=0u);workgroupBarrier();if(found){return;}main(global_id);}@compute @workgroup_size(1)fn validate(@builtin(global_invocation_id)global_id:vec3){main(global_id);}fn main(id:vec3){let m0:vec2=ubo.seed ^ id.xy;let m1:vec2=ubo.blockhash[0u].xy;let m2:vec2=ubo.blockhash[0u].zw;let m3:vec2=ubo.blockhash[1u].xy;let m4:vec2=ubo.blockhash[1u].zw;var v01:vec4=vec4(BLAKE2B_IV_0,0x84CAA73Bu,0xBB67AE85u);var v23:vec4=vec4(0xFE94F82Bu,0x3C6EF372u,0x5F1D36F1u,0xA54FF53Au);var v45:vec4=vec4(0xADE682D1u,0x510E527Fu,0x2B3E6C1Fu,0x9B05688Cu);var v67:vec4=vec4(0xFB41BD6Bu,0x1F83D9ABu,0x137E2179u,0x5BE0CD19u);var v89:vec4=vec4(0xF3BCC908u,0x6A09E667u,0x84CAA73Bu,0xBB67AE85u);var vAB:vec4=vec4(0xFE94F82Bu,0x3C6EF372u,0x5F1D36F1u,0xA54FF53Au);var vCD:vec4=vec4(0xADE682F9u,0x510E527Fu,0x2B3E6C1Fu,0x9B05688Cu);var vEF:vec4=vec4(0x04BE4294u,0xE07C2654u,0x137E2179u,0x5BE0CD19u);var v56:vec4;var vFC:vec4;var v74:vec4;var vDE:vec4;var s0:vec4;var s1:vec4;s0=v01+v45;v01=s0+(vec4(s0(s1(s0(s0(s1>ROTATE_24)|(v45<>ROTATE_24)|(v67<(s0(s1(s0>ROTATE_16)|(vCD<>ROTATE_16)|(vEF<(s0(s1>ROTATE_31).yxwz|(v45<>ROTATE_31).yxwz|(v67<(s0(s1(s0(s1>ROTATE_24)|(v56<>ROTATE_24)|(v74<(s0(s1>ROTATE_16)|(vFC<>ROTATE_16)|(vDE<(s0(s1>ROTATE_31).yxwz|(v56<>ROTATE_31).yxwz|(v74<(s0(s1(s0(s1>ROTATE_24)|(v45<>ROTATE_24)|(v67<(s0(s1>ROTATE_16)|(vCD<>ROTATE_16)|(vEF<(s0(s1>ROTATE_31).yxwz|(v45<>ROTATE_31).yxwz|(v67<(s0(s1(s0(s0(s1>ROTATE_24)|(v56<>ROTATE_24)|(v74<(s0(s1>ROTATE_16)|(vFC<>ROTATE_16)|(vDE<(s0(s1>ROTATE_31).yxwz|(v56<>ROTATE_31).yxwz|(v74<(s0(s1(s0(s1>ROTATE_24)|(v45<>ROTATE_24)|(v67<(s0(s1(s0(s1>ROTATE_16)|(vCD<>ROTATE_16)|(vEF<(s0(s1>ROTATE_31).yxwz|(v45<>ROTATE_31).yxwz|(v67<(s0(s1(s0(s1>ROTATE_24)|(v56<>ROTATE_24)|(v74<(s0(s1>ROTATE_16)|(vFC<>ROTATE_16)|(vDE<(s0(s1>ROTATE_31).yxwz|(v56<>ROTATE_31).yxwz|(v74<(s0(s1(s0(s1>ROTATE_24)|(v45<>ROTATE_24)|(v67<(s0(s1>ROTATE_16)|(vCD<>ROTATE_16)|(vEF<(s0(s1>ROTATE_31).yxwz|(v45<>ROTATE_31).yxwz|(v67<(s0(s1(s0(s1>ROTATE_24)|(v56<>ROTATE_24)|(v74<(s0(s1>ROTATE_16)|(vFC<>ROTATE_16)|(vDE<(s0(s1>ROTATE_31).yxwz|(v56<>ROTATE_31).yxwz|(v74<(s0(s1(s1(s0(s1>ROTATE_24)|(v45<>ROTATE_24)|(v67<(s0(s1>ROTATE_16)|(vCD<>ROTATE_16)|(vEF<(s0(s1>ROTATE_31).yxwz|(v45<>ROTATE_31).yxwz|(v67<(s0(s1(s1(s0(s1>ROTATE_24)|(v56<>ROTATE_24)|(v74<(s0(s1>ROTATE_16)|(vFC<>ROTATE_16)|(vDE<(s0(s1>ROTATE_31).yxwz|(v56<>ROTATE_31).yxwz|(v74<(s0(s1(s0(s1>ROTATE_24)|(v45<>ROTATE_24)|(v67<(s0(s1(s1>ROTATE_16)|(vCD<>ROTATE_16)|(vEF<(s0(s1>ROTATE_31).yxwz|(v45<>ROTATE_31).yxwz|(v67<(s0(s1(s0(s1>ROTATE_24)|(v56<>ROTATE_24)|(v74<(s0(s1>ROTATE_16)|(vFC<>ROTATE_16)|(vDE<(s0(s1>ROTATE_31).yxwz|(v56<>ROTATE_31).yxwz|(v74<(s0(s1(s0(s1>ROTATE_24)|(v45<>ROTATE_24)|(v67<(s0(s1>ROTATE_16)|(vCD<>ROTATE_16)|(vEF<(s0(s1>ROTATE_31).yxwz|(v45<>ROTATE_31).yxwz|(v67<(s0(s1(s0(s1>ROTATE_24)|(v56<>ROTATE_24)|(v74<(s0(s1(s1>ROTATE_16)|(vFC<>ROTATE_16)|(vDE<(s0(s1>ROTATE_31).yxwz|(v56<>ROTATE_31).yxwz|(v74<(s0(s1(s1(s0(s1>ROTATE_24)|(v45<>ROTATE_24)|(v67<(s0(s1>ROTATE_16)|(vCD<>ROTATE_16)|(vEF<(s0(s1>ROTATE_31).yxwz|(v45<>ROTATE_31).yxwz|(v67<(s0(s1(s0(s1>ROTATE_24)|(v56<>ROTATE_24)|(v74<(s0(s1>ROTATE_16)|(vFC<>ROTATE_16)|(vDE<(s0(s1>ROTATE_31).yxwz|(v56<>ROTATE_31).yxwz|(v74<(s0(s1(s0(s1>ROTATE_24)|(v45<>ROTATE_24)|(v67<(s0(s1>ROTATE_16)|(vCD<>ROTATE_16)|(vEF<(s0(s1>ROTATE_31).yxwz|(v45<>ROTATE_31).yxwz|(v67<(s0(s1(s0(s1>ROTATE_24)|(v56<>ROTATE_24)|(v74<(s0(s1>ROTATE_16)|(vFC<>ROTATE_16)|(vDE<(s0(s1>ROTATE_31).yxwz|(v56<>ROTATE_31).yxwz|(v74<(s0(s1(s0(s1>ROTATE_24)|(v45<>ROTATE_24)|(v67<(s0(s1(s0>ROTATE_16)|(vCD<>ROTATE_16)|(vEF<(s0(s1>ROTATE_31).yxwz|(v45<>ROTATE_31).yxwz|(v67<(s0(s1(s0(s1>ROTATE_24)|(v56<>ROTATE_24)|(v74<(s0(s1>ROTATE_16)|(vFC<>ROTATE_16)|(vDE<(s0(s1>ROTATE_31).yxwz|(v56<>ROTATE_31).yxwz|(v74<(s0(s1(s0(s0(s1>ROTATE_24)|(v45<>ROTATE_24)|(v67<(s0(s1(s0>ROTATE_16)|(vCD<>ROTATE_16)|(vEF<(s0(s1>ROTATE_31).yxwz|(v45<>ROTATE_31).yxwz|(v67<(s0(s1(s0(s1>ROTATE_24)|(v56<>ROTATE_24)|(v74<(s0(s1>ROTATE_16)|(vFC<>ROTATE_16)|(vDE<(s0(s1>ROTATE_31).yxwz|(v56<>ROTATE_31).yxwz|(v74<(s0(s1(s0(s1>ROTATE_24)|(v45<>ROTATE_24)|(v67<(s0(s1>ROTATE_16)|(vCD<>ROTATE_16)|(vEF<(s0(s1>ROTATE_31).yxwz|(v45<>ROTATE_31).yxwz|(v67<(s0(s1(s0(s0(s1>ROTATE_24)|(v56<>ROTATE_24)|(v74<(s0(s1>ROTATE_16)|(vDE<(s1ubo.threshold&&atomicLoad(&work.found)==0u){atomicStore(&work.found,1u);work.nonce=m0;}return;}"; - -// src/shaders/gl-downsample.ts -var NanoPowGlDownsampleShader = `#version 300 es -#pragma vscode_glsllint_stage: frag -#ifdef GL_FRAGMENT_PRECISION_HIGH -precision highp float; -#else -precision mediump float; -#endif -precision highp int; - -out uvec4 nonce; - -// source texture to be downsampled -uniform highp usampler2D src; - -void main() { - nonce = uvec4(0u); - vec2 inputSize = vec2(textureSize(src, 0)); - vec2 texel = vec2(1.0) / inputSize; - vec2 blockCoord = (floor(gl_FragCoord.xy) * 2.0 + vec2(0.5)) / inputSize; - - uvec4 pixel = texture(src, blockCoord); - nonce = pixel.x == 0u ? nonce : pixel; - - pixel = texture(src, blockCoord + vec2(texel.x, 0.0)); - nonce = pixel.x == 0u ? nonce : pixel; - - pixel = texture(src, blockCoord + vec2(0.0, texel.y)); - nonce = pixel.x == 0u ? nonce : pixel; - - pixel = texture(src, blockCoord + vec2(texel.x, texel.y)); - nonce = pixel.x == 0u ? nonce : pixel; -} -`; - -// src/shaders/gl-draw.ts -var NanoPowGlDrawShader = `#version 300 es -#pragma vscode_glsllint_stage: frag -#ifdef GL_FRAGMENT_PRECISION_HIGH -precision highp float; -#else -precision mediump float; -#endif - -out uvec4 nonce; - -// blockhash - Array of precalculated block hash components -// threshold - 0xfffffff8 for send/change blocks, 0xfffffe00 for all else -// search - Checks all pixels if true, else only checks 1 pixel to validate -layout(std140) uniform UBO { - uint blockhash[8]; - uint threshold; - bool search; -}; - -// Random work seed values -layout(std140) uniform WORK { - uvec2 seed; -}; - -// Defined separately from uint v[0].y below as the original value is required -// to calculate the second uint32 of the digest for threshold comparison -const uint BLAKE2B_IV32_1 = 0x6A09E667u; - -// Used during G for vector bit rotations -const uvec4 ROTATE_1 = uvec4(1u); -const uvec4 ROTATE_8 = uvec4(8u); -const uvec4 ROTATE_16 = uvec4(16u); -const uvec4 ROTATE_24 = uvec4(24u); -const uvec4 ROTATE_31 = uvec4(31u); - -// 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[12] ^= uint(INLEN); -// It's always the "last" compression at this INLEN -// v[14] = ~v[14]; -const uvec2 blake2b_iv[16] = uvec2[16]( - uvec2(0xF2BDC900u, 0x6A09E667u), - uvec2(0x84CAA73Bu, 0xBB67AE85u), - uvec2(0xFE94F82Bu, 0x3C6EF372u), - uvec2(0x5F1D36F1u, 0xA54FF53Au), - uvec2(0xADE682D1u, 0x510E527Fu), - uvec2(0x2B3E6C1Fu, 0x9B05688Cu), - uvec2(0xFB41BD6Bu, 0x1F83D9ABu), - uvec2(0x137E2179u, 0x5BE0CD19u), - uvec2(0xF3BCC908u, 0x6A09E667u), - uvec2(0x84CAA73Bu, 0xBB67AE85u), - uvec2(0xFE94F82Bu, 0x3C6EF372u), - uvec2(0x5F1D36F1u, 0xA54FF53Au), - uvec2(0xADE682F9u, 0x510E527Fu), - uvec2(0x2B3E6C1Fu, 0x9B05688Cu), - uvec2(0x04BE4294u, 0xE07C2654u), - uvec2(0x137E2179u, 0x5BE0CD19u) -); - -// Iterated initialization vector -uvec2 v[16]; - -// Input data buffer -uvec2 m[16]; - -// G mixing function, compressing two subprocesses into one -void G ( - uint a0, uint b0, uint c0, uint d0, uvec2 x0, uvec2 y0, - uint a1, uint b1, uint c1, uint d1, uvec2 x1, uvec2 y1 -) { - uvec4 a = uvec4(v[a0], v[a1]); - uvec4 b = uvec4(v[b0], v[b1]); - uvec4 c = uvec4(v[c0], v[c1]); - uvec4 d = uvec4(v[d0], v[d1]); - uvec4 mx = uvec4(x0, x1); - uvec4 my = uvec4(y0, y1); - - a = a + b + uvec4(0u, uint(a.x + b.x < a.x), 0u, uint(a.z + b.z < a.z)); - a = a + mx + uvec4(0u, uint(a.x + mx.x < a.x), 0u, uint(a.z + mx.z < a.z)); - d = (d ^ a).yxwz; - c = c + d + uvec4(0u, uint(c.x + d.x < c.x), 0u, uint(c.z + d.z < c.z)); - b = ((b ^ c) >> ROTATE_24) | ((b ^ c) << ROTATE_8).yxwz; - a = a + b + uvec4(0u, uint(a.x + b.x < b.x), 0u, uint(a.z + b.z < b.z)); - a = a + my + uvec4(0u, uint(a.x + my.x < a.x), 0u, uint(a.z + my.z < a.z)); - d = ((d ^ a) >> ROTATE_16) | ((d ^ a) << ROTATE_16).yxwz; - c = c + d + uvec4(0u, uint(c.x + d.x < c.x), 0u, uint(c.z + d.z < c.z)); - b = ((b ^ c) >> ROTATE_31).yxwz | ((b ^ c) << ROTATE_1); - - v[a0] = a.xy; - v[b0] = b.xy; - v[c0] = c.xy; - v[d0] = d.xy; - v[a1] = a.zw; - v[b1] = b.zw; - v[c1] = c.zw; - v[d1] = d.zw; -} - -void main() { - // Initialize fragment output - nonce = uvec4(0u); - - // Nonce uniquely differentiated by pixel location - m[0u] = seed ^ uvec2(gl_FragCoord); - - // Block hash - m[1u] = uvec2(blockhash[0u], blockhash[1u]); - m[2u] = uvec2(blockhash[2u], blockhash[3u]); - m[3u] = uvec2(blockhash[4u], blockhash[5u]); - m[4u] = uvec2(blockhash[6u], blockhash[7u]); - - // Reset v - v = blake2b_iv; - - // Twelve rounds of G mixing - - // Round 0 - G(0u, 4u, 8u, 12u, m[0u], m[1u], 1u, 5u, 9u, 13u, m[2u], m[3u]); - G(2u, 6u, 10u, 14u, m[4u], m[5u], 3u, 7u, 11u, 15u, m[6u], m[7u]); - G(0u, 5u, 10u, 15u, m[8u], m[9u], 1u, 6u, 11u, 12u, m[10u], m[11u]); - G(2u, 7u, 8u, 13u, m[12u], m[13u], 3u, 4u, 9u, 14u, m[14u], m[15u]); - - // Round 1 - G(0u, 4u, 8u, 12u, m[14u], m[10u], 1u, 5u, 9u, 13u, m[4u], m[8u]); - G(2u, 6u, 10u, 14u, m[9u], m[15u], 3u, 7u, 11u, 15u, m[13u], m[6u]); - G(0u, 5u, 10u, 15u, m[1u], m[12u], 1u, 6u, 11u, 12u, m[0u], m[2u]); - G(2u, 7u, 8u, 13u, m[11u], m[7u], 3u, 4u, 9u, 14u, m[5u], m[3u]); - - // Round 2 - G(0u, 4u, 8u, 12u, m[11u], m[8u], 1u, 5u, 9u, 13u, m[12u], m[0u]); - G(2u, 6u, 10u, 14u, m[5u], m[2u], 3u, 7u, 11u, 15u, m[15u], m[13u]); - G(0u, 5u, 10u, 15u, m[10u], m[14u], 1u, 6u, 11u, 12u, m[3u], m[6u]); - G(2u, 7u, 8u, 13u, m[7u], m[1u], 3u, 4u, 9u, 14u, m[9u], m[4u]); - - // Round 3 - G(0u, 4u, 8u, 12u, m[7u], m[9u], 1u, 5u, 9u, 13u, m[3u], m[1u]); - G(2u, 6u, 10u, 14u, m[13u], m[12u], 3u, 7u, 11u, 15u, m[11u], m[14u]); - G(0u, 5u, 10u, 15u, m[2u], m[6u], 1u, 6u, 11u, 12u, m[5u], m[10u]); - G(2u, 7u, 8u, 13u, m[4u], m[0u], 3u, 4u, 9u, 14u, m[15u], m[8u]); - - // Round 4 - G(0u, 4u, 8u, 12u, m[9u], m[0u], 1u, 5u, 9u, 13u, m[5u], m[7u]); - G(2u, 6u, 10u, 14u, m[2u], m[4u], 3u, 7u, 11u, 15u, m[10u], m[15u]); - G(0u, 5u, 10u, 15u, m[14u], m[1u], 1u, 6u, 11u, 12u, m[11u], m[12u]); - G(2u, 7u, 8u, 13u, m[6u], m[8u], 3u, 4u, 9u, 14u, m[3u], m[13u]); - - // Round 5 - G(0u, 4u, 8u, 12u, m[2u], m[12u], 1u, 5u, 9u, 13u, m[6u], m[10u]); - G(2u, 6u, 10u, 14u, m[0u], m[11u], 3u, 7u, 11u, 15u, m[8u], m[3u]); - G(0u, 5u, 10u, 15u, m[4u], m[13u], 1u, 6u, 11u, 12u, m[7u], m[5u]); - G(2u, 7u, 8u, 13u, m[15u], m[14u], 3u, 4u, 9u, 14u, m[1u], m[9u]); - - // Round 6 - G(0u, 4u, 8u, 12u, m[12u], m[5u], 1u, 5u, 9u, 13u, m[1u], m[15u]); - G(2u, 6u, 10u, 14u, m[14u], m[13u], 3u, 7u, 11u, 15u, m[4u], m[10u]); - G(0u, 5u, 10u, 15u, m[0u], m[7u], 1u, 6u, 11u, 12u, m[6u], m[3u]); - G(2u, 7u, 8u, 13u, m[9u], m[2u], 3u, 4u, 9u, 14u, m[8u], m[11u]); - - // Round 7 - G(0u, 4u, 8u, 12u, m[13u], m[11u], 1u, 5u, 9u, 13u, m[7u], m[14u]); - G(2u, 6u, 10u, 14u, m[12u], m[1u], 3u, 7u, 11u, 15u, m[3u], m[9u]); - G(0u, 5u, 10u, 15u, m[5u], m[0u], 1u, 6u, 11u, 12u, m[15u], m[4u]); - G(2u, 7u, 8u, 13u, m[8u], m[6u], 3u, 4u, 9u, 14u, m[2u], m[10u]); - - // Round 8 - G(0u, 4u, 8u, 12u, m[6u], m[15u], 1u, 5u, 9u, 13u, m[14u], m[9u]); - G(2u, 6u, 10u, 14u, m[11u], m[3u], 3u, 7u, 11u, 15u, m[0u], m[8u]); - G(0u, 5u, 10u, 15u, m[12u], m[2u], 1u, 6u, 11u, 12u, m[13u], m[7u]); - G(2u, 7u, 8u, 13u, m[1u], m[4u], 3u, 4u, 9u, 14u, m[10u], m[5u]); - - // Round 9 - G(0u, 4u, 8u, 12u, m[10u], m[2u], 1u, 5u, 9u, 13u, m[8u], m[4u]); - G(2u, 6u, 10u, 14u, m[7u], m[6u], 3u, 7u, 11u, 15u, m[1u], m[5u]); - G(0u, 5u, 10u, 15u, m[15u], m[11u], 1u, 6u, 11u, 12u, m[9u], m[14u]); - G(2u, 7u, 8u, 13u, m[3u], m[12u], 3u, 4u, 9u, 14u, m[13u], m[0u]); - - // Round 10 - G(0u, 4u, 8u, 12u, m[0u], m[1u], 1u, 5u, 9u, 13u, m[2u], m[3u]); - G(2u, 6u, 10u, 14u, m[4u], m[5u], 3u, 7u, 11u, 15u, m[6u], m[7u]); - G(0u, 5u, 10u, 15u, m[8u], m[9u], 1u, 6u, 11u, 12u, m[10u], m[11u]); - G(2u, 7u, 8u, 13u, m[12u], m[13u], 3u, 4u, 9u, 14u, m[14u], m[15u]); - - // Round 11 - G(0u, 4u, 8u, 12u, m[14u], m[10u], 1u, 5u, 9u, 13u, m[4u], m[8u]); - G(2u, 6u, 10u, 14u, m[9u], m[15u], 3u, 7u, 11u, 15u, m[13u], m[6u]); - G(0u, 5u, 10u, 15u, m[1u], m[12u], 1u, 6u, 11u, 12u, m[0u], m[2u]); - G(2u, 7u, 8u, 13u, m[11u], m[7u], 3u, 4u, 9u, 14u, m[5u], m[3u]); - - // Pixel data set from work seed values - // Finalize digest from high bits, low bits can be safely ignored - if ((BLAKE2B_IV32_1 ^ v[0u].y ^ v[8u].y) >= threshold && (search || uvec2(gl_FragCoord) == uvec2(0u))) { - nonce = uvec4(1u, m[0u].y, m[0u].x, (uint(gl_FragCoord.x) << 16u) | uint(gl_FragCoord.y)); - } - - // Valid nonce not found - if (nonce.x == 0u) { - discard; - } -} -`; - -// src/shaders/gl-vertex.ts -var NanoPowGlVertexShader = `#version 300 es -#pragma vscode_glsllint_stage: vert -#ifdef GL_FRAGMENT_PRECISION_HIGH -precision highp float; -#else -precision mediump float; -#endif - -layout (location=0) in vec4 position; - -void main() { - gl_Position = position; -} -`; - -// src/classes/gl.ts -var NanoPowGl = class _NanoPowGl { - static #busy = false; - static #debug = false; - static #raf = 0; - /** Used to set canvas size. */ - static #cores = Math.max(1, Math.floor(navigator.hardwareConcurrency)); - static #WORKLOAD = 256 * this.#cores; - static #canvas = new OffscreenCanvas(this.#WORKLOAD, this.#WORKLOAD); - static get size() { - return this.#gl?.drawingBufferWidth; - } - static #gl; - static #drawProgram; - static #downsampleProgram; - static #vertexShader; - static #drawShader; - static #downsampleShader; - static #positionBuffer; - static #drawFbo; - static #downsampleFbos = []; - static #downsampleSrcLocation; - static #uboBuffer; - static #uboView = new DataView(new ArrayBuffer(144)); - static #seedBuffer; - static #seed = new BigUint64Array(1); - static #query; - static #pixels; - /**Vertex Positions, 2 triangles */ - static #positions = new Float32Array([ - -1, - -1, - 1, - -1, - 1, - 1, - -1, - 1 - ]); - /** Compile */ - static async init() { - if (this.#busy) return; - this.#busy = true; - try { - this.#canvas.addEventListener("webglcontextlost", (event) => { - event.preventDefault(); - console.warn("WebGL context lost. Waiting for it to be restored..."); - cancelAnimationFrame(this.#raf); - }, false); - this.#canvas.addEventListener("webglcontextrestored", (event) => { - console.warn("WebGL context restored. Reinitializing..."); - _NanoPowGl.init(); - }, false); - this.#gl = this.#canvas.getContext("webgl2"); - if (this.#gl == null) throw new Error("WebGL 2 is required"); - this.#drawProgram = this.#gl.createProgram(); - if (this.#drawProgram == 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, NanoPowGlVertexShader); - 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.#drawShader = this.#gl.createShader(this.#gl.FRAGMENT_SHADER); - if (this.#drawShader == null) throw new Error("Failed to create fragment shader"); - this.#gl.shaderSource(this.#drawShader, NanoPowGlDrawShader); - this.#gl.compileShader(this.#drawShader); - if (!this.#gl.getShaderParameter(this.#drawShader, this.#gl.COMPILE_STATUS)) - throw new Error(this.#gl.getShaderInfoLog(this.#drawShader) ?? `Failed to compile fragment shader`); - this.#gl.attachShader(this.#drawProgram, this.#vertexShader); - this.#gl.attachShader(this.#drawProgram, this.#drawShader); - this.#gl.linkProgram(this.#drawProgram); - if (!this.#gl.getProgramParameter(this.#drawProgram, this.#gl.LINK_STATUS)) - throw new Error(this.#gl.getProgramInfoLog(this.#drawProgram) ?? `Failed to link program`); - this.#downsampleProgram = this.#gl.createProgram(); - if (this.#downsampleProgram == null) throw new Error("Failed to create downsample program"); - this.#downsampleShader = this.#gl.createShader(this.#gl.FRAGMENT_SHADER); - if (this.#downsampleShader == null) throw new Error("Failed to create downsample shader"); - this.#gl.shaderSource(this.#downsampleShader, NanoPowGlDownsampleShader); - this.#gl.compileShader(this.#downsampleShader); - if (!this.#gl.getShaderParameter(this.#downsampleShader, this.#gl.COMPILE_STATUS)) - throw new Error(this.#gl.getShaderInfoLog(this.#downsampleShader) ?? `Failed to compile downsample shader`); - this.#gl.attachShader(this.#downsampleProgram, this.#vertexShader); - this.#gl.attachShader(this.#downsampleProgram, this.#downsampleShader); - this.#gl.linkProgram(this.#downsampleProgram); - if (!this.#gl.getProgramParameter(this.#downsampleProgram, this.#gl.LINK_STATUS)) - throw new Error(this.#gl.getProgramInfoLog(this.#downsampleProgram) ?? `Failed to link program`); - this.#gl.useProgram(this.#drawProgram); - 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, 2, this.#gl.FLOAT, false, 0, 0); - this.#gl.enableVertexAttribArray(0); - this.#gl.bindBuffer(this.#gl.ARRAY_BUFFER, null); - const texture = this.#gl.createTexture(); - this.#gl.bindTexture(this.#gl.TEXTURE_2D, texture); - this.#gl.texImage2D(this.#gl.TEXTURE_2D, 0, this.#gl.RGBA32UI, this.#gl.drawingBufferWidth, this.#gl.drawingBufferHeight, 0, this.#gl.RGBA_INTEGER, this.#gl.UNSIGNED_INT, null); - this.#gl.texParameteri(this.#gl.TEXTURE_2D, this.#gl.TEXTURE_MIN_FILTER, this.#gl.NEAREST); - this.#gl.texParameteri(this.#gl.TEXTURE_2D, this.#gl.TEXTURE_MAG_FILTER, this.#gl.NEAREST); - const framebuffer = this.#gl.createFramebuffer(); - this.#gl.bindFramebuffer(this.#gl.FRAMEBUFFER, framebuffer); - this.#gl.framebufferTexture2D(this.#gl.FRAMEBUFFER, this.#gl.COLOR_ATTACHMENT0, this.#gl.TEXTURE_2D, texture, 0); - if (this.#gl.checkFramebufferStatus(this.#gl.FRAMEBUFFER) !== this.#gl.FRAMEBUFFER_COMPLETE) - throw new Error(`Failed to create drawing framebuffer`); - this.#drawFbo = { texture, framebuffer, size: { x: this.#gl.drawingBufferWidth, y: this.#gl.drawingBufferHeight } }; - for (let i = 1; i <= 4; i++) { - const width = this.#gl.drawingBufferWidth / 2 ** i; - const height = this.#gl.drawingBufferHeight / 2 ** i; - const texture2 = this.#gl.createTexture(); - this.#gl.bindTexture(this.#gl.TEXTURE_2D, texture2); - this.#gl.texImage2D(this.#gl.TEXTURE_2D, 0, this.#gl.RGBA32UI, width, height, 0, this.#gl.RGBA_INTEGER, this.#gl.UNSIGNED_INT, null); - this.#gl.texParameteri(this.#gl.TEXTURE_2D, this.#gl.TEXTURE_MIN_FILTER, this.#gl.NEAREST); - this.#gl.texParameteri(this.#gl.TEXTURE_2D, this.#gl.TEXTURE_MAG_FILTER, this.#gl.NEAREST); - const framebuffer2 = this.#gl.createFramebuffer(); - this.#gl.bindFramebuffer(this.#gl.FRAMEBUFFER, framebuffer2); - this.#gl.framebufferTexture2D(this.#gl.FRAMEBUFFER, this.#gl.COLOR_ATTACHMENT0, this.#gl.TEXTURE_2D, texture2, 0); - if (this.#gl.checkFramebufferStatus(this.#gl.FRAMEBUFFER) !== this.#gl.FRAMEBUFFER_COMPLETE) - throw new Error(`Failed to create downsampling framebuffer ${i}`); - this.#downsampleFbos.push({ texture: texture2, framebuffer: framebuffer2, size: { x: width, y: height } }); - } - this.#downsampleSrcLocation = this.#gl.getUniformLocation(this.#downsampleProgram, "src"); - this.#gl.bindTexture(this.#gl.TEXTURE_2D, null); - this.#gl.bindFramebuffer(this.#gl.FRAMEBUFFER, null); - this.#uboBuffer = this.#gl.createBuffer(); - this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, this.#uboBuffer); - this.#gl.bufferData(this.#gl.UNIFORM_BUFFER, 144, this.#gl.DYNAMIC_DRAW); - this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, null); - this.#gl.bindBufferBase(this.#gl.UNIFORM_BUFFER, 0, this.#uboBuffer); - this.#gl.uniformBlockBinding(this.#drawProgram, this.#gl.getUniformBlockIndex(this.#drawProgram, "UBO"), 0); - this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, null); - this.#seedBuffer = this.#gl.createBuffer(); - this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, this.#seedBuffer); - this.#gl.bufferData(this.#gl.UNIFORM_BUFFER, 16, this.#gl.DYNAMIC_DRAW); - this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, null); - this.#gl.bindBufferBase(this.#gl.UNIFORM_BUFFER, 1, this.#seedBuffer); - this.#gl.uniformBlockBinding(this.#drawProgram, this.#gl.getUniformBlockIndex(this.#drawProgram, "WORK"), 1); - this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, null); - this.#query = this.#gl.createQuery(); - this.#pixels = new Uint32Array(this.#gl.drawingBufferWidth * this.#gl.drawingBufferHeight * 4); - console.log(`NanoPow WebGL initialized at ${this.#gl.drawingBufferWidth}x${this.#gl.drawingBufferHeight}. Maximum nonces checked per frame: ${this.#gl.drawingBufferWidth * this.#gl.drawingBufferHeight}`); - } catch (err) { - throw new Error("WebGL initialization failed.", { cause: err }); - } finally { - this.#busy = false; - } - } - static reset() { - cancelAnimationFrame(_NanoPowGl.#raf); - _NanoPowGl.#gl?.deleteQuery(_NanoPowGl.#query); - _NanoPowGl.#query = null; - _NanoPowGl.#gl?.deleteBuffer(_NanoPowGl.#seedBuffer); - _NanoPowGl.#seedBuffer = null; - _NanoPowGl.#gl?.deleteBuffer(_NanoPowGl.#uboBuffer); - _NanoPowGl.#uboBuffer = null; - for (const fbo of _NanoPowGl.#downsampleFbos) { - _NanoPowGl.#gl?.deleteFramebuffer(fbo.framebuffer); - _NanoPowGl.#gl?.deleteTexture(fbo.texture); - } - _NanoPowGl.#downsampleFbos = []; - _NanoPowGl.#gl?.deleteShader(_NanoPowGl.#downsampleShader); - _NanoPowGl.#downsampleShader = null; - _NanoPowGl.#gl?.deleteProgram(_NanoPowGl.#downsampleProgram); - _NanoPowGl.#downsampleProgram = null; - _NanoPowGl.#gl?.deleteFramebuffer(_NanoPowGl.#drawFbo?.framebuffer ?? null); - _NanoPowGl.#drawFbo = null; - _NanoPowGl.#gl?.deleteTexture(_NanoPowGl.#drawFbo); - _NanoPowGl.#drawFbo = null; - _NanoPowGl.#gl?.deleteBuffer(_NanoPowGl.#positionBuffer); - _NanoPowGl.#positionBuffer = null; - _NanoPowGl.#gl?.deleteShader(_NanoPowGl.#drawShader); - _NanoPowGl.#drawShader = null; - _NanoPowGl.#gl?.deleteShader(_NanoPowGl.#vertexShader); - _NanoPowGl.#vertexShader = null; - _NanoPowGl.#gl?.deleteProgram(_NanoPowGl.#drawProgram); - _NanoPowGl.#drawProgram = null; - _NanoPowGl.#gl = null; - _NanoPowGl.#busy = false; - _NanoPowGl.init(); - } - static #logAverages(times) { - let count = times.length, sum = 0, reciprocals = 0, logarithms = 0, truncated = 0, min = 65535, max = 0, rate = 0; - times.sort(); - for (let i = 0; i < count; i++) { - sum += times[i]; - reciprocals += 1 / times[i]; - logarithms += Math.log(times[i]); - min = Math.min(min, times[i]); - max = Math.max(max, times[i]); - if (count < 3 || i > count * 0.1 && i < count * 0.9) truncated += times[i]; - } - const averages = { - "Count (frames)": count, - "Total (ms)": sum, - "Rate (f/s)": 1e3 * count * 0.8 / (truncated || sum), - "Minimum (ms)": min, - "Maximum (ms)": max, - "Arithmetic Mean (ms)": sum / count, - "Truncated Mean (ms)": truncated / count, - "Harmonic Mean (ms)": count / reciprocals, - "Geometric Mean (ms)": Math.exp(logarithms / count) - }; - console.log(`Averages: ${JSON.stringify(averages)}`); - console.table(averages); - } - static #draw(seed) { - if (this.#gl == null || this.#query == null) throw new Error("WebGL 2 is required to draw and query pixels"); - if (this.#drawFbo == null) throw new Error("FBO is required to draw"); - if (this.#seed[0] == null || this.#seedBuffer == null) throw new Error("Seed is required to draw"); - this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, this.#seedBuffer); - this.#gl.bufferSubData(this.#gl.UNIFORM_BUFFER, 0, seed); - this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, null); - this.#gl.useProgram(this.#drawProgram); - this.#gl.bindFramebuffer(this.#gl.FRAMEBUFFER, this.#drawFbo.framebuffer); - this.#gl.activeTexture(this.#gl.TEXTURE0); - this.#gl.bindTexture(this.#gl.TEXTURE_2D, this.#drawFbo.texture); - this.#gl.beginQuery(this.#gl.ANY_SAMPLES_PASSED_CONSERVATIVE, this.#query); - this.#gl.viewport(0, 0, this.#drawFbo.size.x, this.#drawFbo.size.y); - this.#gl.drawArrays(this.#gl.TRIANGLES, 0, 4); - this.#gl.bindFramebuffer(this.#gl.FRAMEBUFFER, null); - this.#gl.endQuery(this.#gl.ANY_SAMPLES_PASSED_CONSERVATIVE); - } - static async #checkQueryResult() { - return new Promise((resolve, reject) => { - function check() { - try { - if (_NanoPowGl.#gl == null || _NanoPowGl.#query == null) throw new Error("WebGL 2 is required to check query results"); - if (_NanoPowGl.#gl.getQueryParameter(_NanoPowGl.#query, _NanoPowGl.#gl.QUERY_RESULT_AVAILABLE)) { - resolve(!!_NanoPowGl.#gl.getQueryParameter(_NanoPowGl.#query, _NanoPowGl.#gl.QUERY_RESULT)); - } else { - _NanoPowGl.#raf = requestAnimationFrame(check); - } - } catch (err) { - reject(err); - } - } - check(); - }); - } - /** - * 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 workHex - Original nonce if provided for a validation call - * @returns Nonce as an 8-byte (16-char) hexadecimal string - */ - static #readResult(workHex) { - if (this.#gl == null) throw new Error("WebGL 2 is required to read pixels"); - if (this.#drawFbo == null) throw new Error("Source FBO is required to downsample"); - let source = this.#drawFbo; - let pixelCount; - const start = performance.now(); - if (workHex != null) { - this.#gl.bindFramebuffer(this.#gl.FRAMEBUFFER, source.framebuffer); - this.#gl.readPixels(0, 0, 1, 1, this.#gl.RGBA_INTEGER, this.#gl.UNSIGNED_INT, this.#pixels); - pixelCount = 4; - } else { - this.#gl.useProgram(this.#downsampleProgram); - for (const fbo of this.#downsampleFbos) { - this.#gl.bindFramebuffer(this.#gl.FRAMEBUFFER, fbo.framebuffer); - this.#gl.activeTexture(this.#gl.TEXTURE0); - this.#gl.bindTexture(this.#gl.TEXTURE_2D, source.texture); - this.#gl.uniform1i(this.#downsampleSrcLocation, 0); - this.#gl.viewport(0, 0, fbo.size.x, fbo.size.y); - this.#gl.drawArrays(this.#gl.TRIANGLES, 0, 4); - source = fbo; - } - this.#gl.bindFramebuffer(this.#gl.FRAMEBUFFER, source.framebuffer); - this.#gl.readPixels(0, 0, source.size.x, source.size.y, this.#gl.RGBA_INTEGER, this.#gl.UNSIGNED_INT, this.#pixels); - pixelCount = source.size.x * source.size.y * 4; - } - this.#gl.bindFramebuffer(this.#gl.FRAMEBUFFER, null); - for (let i = 0; i < pixelCount; i += 4) { - if (this.#pixels[i] !== 0) { - if (this.#debug) console.log(`readResults (${performance.now() - start} ms)`); - if (this.#debug) console.log(`Pixel: rgba(${this.#pixels[i]}, ${this.#pixels[i + 1]}, ${this.#pixels[i + 2]}, ${this.#pixels[i + 3].toString(16).padStart(8, "0")})`); - const hex = `${this.#pixels[i + 1].toString(16).padStart(8, "0")}${this.#pixels[i + 2].toString(16).padStart(8, "0")}`; - if (workHex == null || workHex == hex) return hex; - } - } - throw new Error("Query reported result but nonce value not found"); - } - /** - * 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, options) { - if (this.#busy) { - console.log("NanoPowGl is busy. Retrying search..."); - return new Promise((resolve) => { - setTimeout(async () => { - const result = this.search(hash, options); - resolve(result); - }, 100); - }); - } - this.#busy = true; - if (!/^[A-Fa-f0-9]{64}$/.test(hash)) throw new Error(`Invalid hash ${hash}`); - const threshold = typeof options?.threshold !== "number" || options.threshold < 0 || options.threshold > 4294967295 ? 4294967288 : options.threshold; - const effort = typeof options?.effort !== "number" || options.effort < 1 || options.effort > 32 ? this.#cores : options.effort; - this.#debug = !!options?.debug; - if (this.#WORKLOAD !== 256 * effort) { - this.#WORKLOAD = 256 * effort; - this.#canvas.height = this.#WORKLOAD; - this.#canvas.width = this.#WORKLOAD; - this.reset(); - } - if (_NanoPowGl.#gl == null) throw new Error("WebGL 2 is required"); - if (this.#gl == null) throw new Error("WebGL 2 is required"); - if (this.#drawFbo == null) throw new Error("WebGL framebuffer is required"); - this.#gl.bindFramebuffer(this.#gl.FRAMEBUFFER, this.#drawFbo.framebuffer); - this.#gl.clearBufferuiv(this.#gl.COLOR, 0, [0, 0, 0, 0]); - this.#gl.bindFramebuffer(this.#gl.FRAMEBUFFER, null); - for (let i = 0; i < this.#uboView.byteLength; i++) this.#uboView.setUint8(i, 0); - for (let i = 0; i < 64; i += 8) { - const uint32 = hash.slice(i, i + 8); - this.#uboView.setUint32(i * 2, parseInt(uint32, 16)); - } - this.#uboView.setUint32(128, threshold, true); - this.#uboView.setUint32(132, 1, true); - if (this.#debug) console.log("UBO", this.#uboView.buffer.slice(0)); - this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, this.#uboBuffer); - this.#gl.bufferSubData(this.#gl.UNIFORM_BUFFER, 0, this.#uboView); - this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, null); - let times = []; - let start = performance.now(); - let nonce = null; - if (this.#debug) console.groupCollapsed("Seeds (click to view)"); - while (nonce == null) { - start = performance.now(); - const random0 = Math.floor(Math.random() * 4294967295); - const random1 = Math.floor(Math.random() * 4294967295); - this.#seed[0] = BigInt(random0) << 32n | BigInt(random1); - if (this.#debug) console.log("Seed", this.#seed); - this.#draw(this.#seed); - const found = await this.#checkQueryResult(); - times.push(performance.now() - start); - if (found) { - if (this.#debug) console.groupEnd(); - nonce = this.#readResult(); - } - } - this.#busy = false; - if (this.#debug) this.#logAverages(times); - return nonce; - } - /** - * Validates that a nonce satisfies Nano proof-of-work requirements. - * - * @param {string} work - Hexadecimal proof-of-work value to validate - * @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 validate(work, hash, options) { - if (this.#busy) { - console.log("NanoPowGl is busy. Retrying validate..."); - return new Promise((resolve) => { - setTimeout(async () => { - const result = this.validate(work, hash, options); - resolve(result); - }, 100); - }); - } - this.#busy = true; - if (!/^[A-Fa-f0-9]{16}$/.test(work)) throw new Error(`Invalid work ${work}`); - if (!/^[A-Fa-f0-9]{64}$/.test(hash)) throw new Error(`Invalid hash ${hash}`); - const threshold = typeof options?.threshold !== "number" || options.threshold < 0 || options.threshold > 4294967295 ? 4294967288 : options.threshold; - this.#debug = !!options?.debug; - if (_NanoPowGl.#gl == null) throw new Error("WebGL 2 is required"); - if (this.#gl == null) throw new Error("WebGL 2 is required"); - if (this.#drawFbo == null) throw new Error("WebGL framebuffer is required"); - this.#gl.bindFramebuffer(this.#gl.FRAMEBUFFER, this.#drawFbo.framebuffer); - this.#gl.clearBufferuiv(this.#gl.COLOR, 0, [0, 0, 0, 0]); - this.#gl.bindFramebuffer(this.#gl.FRAMEBUFFER, null); - for (let i = 0; i < this.#uboView.byteLength; i++) this.#uboView.setUint8(i, 0); - for (let i = 0; i < 64; i += 8) { - const uint32 = hash.slice(i, i + 8); - this.#uboView.setUint32(i * 2, parseInt(uint32, 16)); - } - this.#uboView.setUint32(128, threshold, true); - this.#uboView.setUint32(132, 0, true); - if (this.#debug) console.log("UBO", this.#uboView.buffer.slice(0)); - this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, this.#uboBuffer); - this.#gl.bufferSubData(this.#gl.UNIFORM_BUFFER, 0, this.#uboView); - this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, null); - let nonce = null; - this.#seed[0] = BigInt(`0x${work}`); - if (this.#debug) console.log("Work", this.#seed); - this.#draw(this.#seed); - let found = await this.#checkQueryResult(); - if (found) { - try { - nonce = this.#readResult(work); - } catch (err) { - found = false; - } - } - this.#busy = false; - if (found && nonce !== work) throw new Error(`Nonce found but does not match work`); - return found; - } -}; - -// src/classes/gpu.ts -var NanoPowGpu = class _NanoPowGpu { - // Initialize WebGPU - static #busy = false; - static #debug = false; - static #device = null; - static #gpuBufferReset = new BigUint64Array([0n, 0n]); - static #gpuBuffer; - static #cpuBuffer; - static #uboBuffer; - static #uboView; - static #bindGroupLayout; - static #searchPipeline; - static #validatePipeline; - // Initialize WebGPU - static async init() { - if (this.#busy) return; - this.#busy = true; - try { - if (navigator.gpu == null) throw new Error("WebGPU is not supported in this browser."); - const adapter = await navigator.gpu.requestAdapter(); - if (adapter == null) throw new Error("WebGPU adapter refused by browser."); - const device = await adapter.requestDevice(); - if (!(device instanceof GPUDevice)) throw new Error("WebGPU device failed to load."); - device.lost.then(this.reset); - this.#device = device; - this.setup(); - } catch (err) { - throw new Error("WebGPU initialization failed.", { cause: err }); - } finally { - this.#busy = false; - } - } - static setup() { - if (this.#device == null) throw new Error(`WebGPU device failed to load.`); - this.#gpuBuffer = this.#device.createBuffer({ - size: 16, - usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC - }); - this.#cpuBuffer = this.#device.createBuffer({ - size: 16, - usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ - }); - this.#uboBuffer = this.#device.createBuffer({ - size: 48, - usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST - }); - this.#uboView = new DataView(new ArrayBuffer(48)); - this.#bindGroupLayout = this.#device.createBindGroupLayout({ - entries: [ - { - binding: 0, - visibility: GPUShaderStage.COMPUTE, - buffer: { type: "uniform" } - }, - { - binding: 1, - visibility: GPUShaderStage.COMPUTE, - buffer: { type: "storage" } - } - ] - }); - const shaderModule = this.#device.createShaderModule({ - code: compute_default - }); - this.#searchPipeline = this.#device.createComputePipeline({ - layout: this.#device.createPipelineLayout({ - bindGroupLayouts: [this.#bindGroupLayout] - }), - compute: { - entryPoint: "search", - module: shaderModule - } - }); - this.#validatePipeline = this.#device.createComputePipeline({ - layout: this.#device.createPipelineLayout({ - bindGroupLayouts: [this.#bindGroupLayout] - }), - compute: { - entryPoint: "validate", - module: shaderModule - } - }); - console.log(`NanoPow WebGPU initialized. Recommended effort: ${Math.max(1, Math.floor(navigator.hardwareConcurrency / 2))}`); - } - static reset() { - console.warn(`GPU device lost. Reinitializing...`); - _NanoPowGpu.#cpuBuffer?.destroy(); - _NanoPowGpu.#gpuBuffer?.destroy(); - _NanoPowGpu.#uboBuffer?.destroy(); - _NanoPowGpu.#busy = false; - _NanoPowGpu.init(); - } - static #logAverages(times) { - let count = times.length, truncatedCount = 0, truncated = 0, sum = 0, reciprocals = 0, logarithms = 0, min = Number.MAX_SAFE_INTEGER, max = 0, median = 0, rate = 0; - times.sort(); - for (let i = 0; i < count; i++) { - sum += times[i]; - reciprocals += 1 / times[i]; - logarithms += Math.log(times[i]); - min = Math.min(min, times[i]); - max = Math.max(max, times[i]); - if (i === Math.ceil(count / 2)) { - median = times[i]; - } - if (count < 3 || i > 0.1 * count && i < 0.9 * (count - 1)) { - truncated += times[i]; - truncatedCount++; - } - } - const averages = { - "Count (dispatches)": count, - "Total (ms)": sum, - "Rate (d/s)": 1e3 * truncatedCount / (truncated || sum), - "Minimum (ms)": min, - "Maximum (ms)": max, - "Median (ms)": median, - "Arithmetic Mean (ms)": sum / count, - "Truncated Mean (ms)": truncated / truncatedCount, - "Harmonic Mean (ms)": count / reciprocals, - "Geometric Mean (ms)": Math.exp(logarithms / count) - }; - console.table(averages); - } - static async #dispatch(pipeline, seed, hash, threshold, passes) { - if (this.#device == null) throw new Error(`WebGPU device failed to load.`); - for (let i = 0; i < this.#uboView.byteLength; i++) this.#uboView.setUint8(i, 0); - for (let i = 0; i < 64; i += 16) { - const u64 = hash.slice(i, i + 16); - this.#uboView.setBigUint64(i / 2, BigInt(`0x${u64}`)); - } - this.#uboView.setBigUint64(32, seed, true); - this.#uboView.setUint32(40, threshold, true); - if (this.#debug) console.log("UBO", this.#uboView); - this.#device.queue.writeBuffer(this.#uboBuffer, 0, this.#uboView); - this.#device.queue.writeBuffer(this.#gpuBuffer, 0, this.#gpuBufferReset); - const bindGroup = this.#device.createBindGroup({ - layout: this.#bindGroupLayout, - entries: [ - { - binding: 0, - resource: { - buffer: this.#uboBuffer - } - }, - { - binding: 1, - resource: { - buffer: this.#gpuBuffer - } - } - ] - }); - const commandEncoder = this.#device.createCommandEncoder(); - const passEncoder = commandEncoder.beginComputePass(); - passEncoder.setPipeline(pipeline); - passEncoder.setBindGroup(0, bindGroup); - passEncoder.dispatchWorkgroups(passes, passes); - passEncoder.end(); - commandEncoder.copyBufferToBuffer(this.#gpuBuffer, 0, this.#cpuBuffer, 0, 12); - this.#device.queue.submit([commandEncoder.finish()]); - let data = null; - try { - await this.#cpuBuffer.mapAsync(GPUMapMode.READ); - await this.#device.queue.onSubmittedWorkDone(); - data = new DataView(this.#cpuBuffer.getMappedRange().slice(0)); - this.#cpuBuffer.unmap(); - } catch (err) { - console.warn(`Error getting data from GPU. ${err}`); - return this.#dispatch(pipeline, seed, hash, threshold, passes); - } - if (this.#debug) console.log("gpuBuffer data", data); - if (data == null) throw new Error(`Failed to get data from buffer.`); - return data; - } - /** - * 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 {NanoPowOptions} options - Used to configure search execution - */ - static async search(hash, options) { - 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 () => { - const result = this.search(hash, options); - resolve(result); - }, 100); - }); - } - this.#busy = true; - const threshold = typeof options?.threshold !== "number" || options.threshold < 0 || options.threshold > 4294967295 ? 4294967288 : options.threshold; - const effort = typeof options?.effort !== "number" || options.effort < 1 || options.effort > 32 ? 2048 : options.effort * 256; - this.#debug = !!options?.debug; - 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.`); - } - let times = []; - let start = performance.now(); - let nonce = 0n; - do { - start = performance.now(); - const random0 = Math.floor(Math.random() * 4294967295); - const random1 = Math.floor(Math.random() * 4294967295); - const seed = BigInt(random0) << 32n | BigInt(random1); - if (this.#debug) console.log(`seed: ${seed}`); - const data = await this.#dispatch(this.#searchPipeline, seed, hash, threshold, effort); - nonce = data.getBigUint64(0, true); - this.#busy = !data.getUint32(8); - times.push(performance.now() - start); - } while (this.#busy); - if (this.#debug) this.#logAverages(times); - return nonce.toString(16).padStart(16, "0"); - } - /** - * Validates that a nonce satisfies Nano proof-of-work requirements. - * - * @param {string} work - Hexadecimal proof-of-work value to validate - * @param {string} hash - Hexadecimal hash of previous block, or public key for new accounts - * @param {NanoPowOptions} options - Options used to configure search execution - */ - static async validate(work, hash, options) { - 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 () => { - const result = this.validate(work, hash, options); - resolve(result); - }, 100); - }); - } - this.#busy = true; - this.#debug = !!options?.debug; - const threshold = typeof options?.threshold !== "number" || options.threshold < 0 || options.threshold > 4294967295 ? 4294967288 : options.threshold; - 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 seed = BigInt(`0x${work}`); - if (this.#debug) console.log(`work: ${work}`); - const data = await this.#dispatch(this.#validatePipeline, seed, hash, threshold, 1); - const nonce = data.getBigUint64(0, true).toString(16).padStart(16, "0"); - if (this.#debug) console.log(`nonce: ${nonce}`); - const found = !!data.getUint32(8); - this.#busy = false; - if (found && work !== nonce) throw new Error(`Nonce (${nonce}) found but does not match work (${work})`); - return found; - } -}; - -// src/classes/index.ts -var isGlSupported; -var isGpuSupported = false; -try { - await NanoPowGpu.init(); - isGpuSupported = true; -} catch (err) { - console.warn("WebGPU is not supported in this environment.\n", err); - isGpuSupported = false; -} -try { - await NanoPowGl.init(); - isGlSupported = true; -} catch (err) { - console.warn("WebGL is not supported in this environment.\n", err); - isGlSupported = false; -} -var NanoPow = isGpuSupported ? NanoPowGpu : isGlSupported ? NanoPowGl : null; - -// src/main.ts -var main_default = NanoPow; -export { - NanoPow, - NanoPowGl, - NanoPowGpu, - main_default as default -}; -- 2.34.1