nano-pow 1.2.2 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/global.min.js +2 -2
- package/dist/main.min.js +2 -2
- package/dist/types.d.ts +53 -0
- package/package.json +7 -8
- package/dist/classes/gl.d.ts +0 -20
- package/dist/classes/gl.js +0 -232
- package/dist/classes/gpu.d.ts +0 -24
- package/dist/classes/gpu.js +0 -236
- package/dist/classes/index.d.ts +0 -4
- package/dist/classes/index.js +0 -23
- package/dist/global.d.ts +0 -1
- package/dist/global.js +0 -6
- package/dist/main.d.ts +0 -3
- package/dist/main.js +0 -5
- package/dist/shaders/gl-fragment.d.ts +0 -1
- package/dist/shaders/gl-fragment.js +0 -206
- package/dist/shaders/gl-vertex.d.ts +0 -1
- package/dist/shaders/gl-vertex.js +0 -16
- package/dist/shaders/index.d.ts +0 -4
- package/dist/shaders/index.js +0 -6
package/dist/global.min.js
CHANGED
|
@@ -606,7 +606,7 @@ var NanoPowGpu = class _NanoPowGpu {
|
|
|
606
606
|
do {
|
|
607
607
|
const random = Math.floor(Math.random() * 4294967295);
|
|
608
608
|
const seed = BigInt(random) << 32n | BigInt(random);
|
|
609
|
-
const data = await this.#dispatch(seed, hash, threshold,
|
|
609
|
+
const data = await this.#dispatch(seed, hash, threshold, 1024);
|
|
610
610
|
nonce = data.getBigUint64(0, true);
|
|
611
611
|
this.#busy = !data.getUint32(8);
|
|
612
612
|
} while (this.#busy);
|
|
@@ -668,7 +668,7 @@ try {
|
|
|
668
668
|
}
|
|
669
669
|
var NanoPow = isGpuSupported ? NanoPowGpu : isGlSupported ? NanoPowGl : null;
|
|
670
670
|
|
|
671
|
-
//
|
|
671
|
+
// src/global.ts
|
|
672
672
|
globalThis.NanoPow ??= NanoPow;
|
|
673
673
|
globalThis.NanoPowGl ??= NanoPowGl;
|
|
674
674
|
globalThis.NanoPowGpu ??= NanoPowGpu;
|
package/dist/main.min.js
CHANGED
|
@@ -606,7 +606,7 @@ var NanoPowGpu = class _NanoPowGpu {
|
|
|
606
606
|
do {
|
|
607
607
|
const random = Math.floor(Math.random() * 4294967295);
|
|
608
608
|
const seed = BigInt(random) << 32n | BigInt(random);
|
|
609
|
-
const data = await this.#dispatch(seed, hash, threshold,
|
|
609
|
+
const data = await this.#dispatch(seed, hash, threshold, 1024);
|
|
610
610
|
nonce = data.getBigUint64(0, true);
|
|
611
611
|
this.#busy = !data.getUint32(8);
|
|
612
612
|
} while (this.#busy);
|
|
@@ -668,7 +668,7 @@ try {
|
|
|
668
668
|
}
|
|
669
669
|
var NanoPow = isGpuSupported ? NanoPowGpu : isGlSupported ? NanoPowGl : null;
|
|
670
670
|
|
|
671
|
-
//
|
|
671
|
+
// src/main.ts
|
|
672
672
|
var main_default = NanoPow;
|
|
673
673
|
export {
|
|
674
674
|
NanoPow,
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import "@webgpu/types"
|
|
2
|
+
|
|
3
|
+
export declare const NanoPowGlFragmentShader: string
|
|
4
|
+
export declare const NanoPowGlVertexShader: string
|
|
5
|
+
export declare const NanoPowGpuComputeShader: any
|
|
6
|
+
|
|
7
|
+
declare const NanoPow: typeof NanoPowGl | typeof NanoPowGpu | null
|
|
8
|
+
|
|
9
|
+
export declare class NanoPowGl {
|
|
10
|
+
#private
|
|
11
|
+
/** Compile */
|
|
12
|
+
static init (): Promise<void>
|
|
13
|
+
/**
|
|
14
|
+
* Finds a nonce that satisfies the Nano proof-of-work requirements.
|
|
15
|
+
*
|
|
16
|
+
* @param {string} hash - Hexadecimal hash of previous block, or public key for new accounts
|
|
17
|
+
* @param {number} [threshold=0xfffffff8] - Difficulty of proof-of-work calculation
|
|
18
|
+
*/
|
|
19
|
+
static search (hash: string, threshold?: number): Promise<string>
|
|
20
|
+
/**
|
|
21
|
+
* Validates that a nonce satisfies Nano proof-of-work requirements.
|
|
22
|
+
*
|
|
23
|
+
* @param {string} work - Hexadecimal proof-of-work value to validate
|
|
24
|
+
* @param {string} hash - Hexadecimal hash of previous block, or public key for new accounts
|
|
25
|
+
* @param {number} [threshold=0xfffffff8] - Difficulty of proof-of-work calculation
|
|
26
|
+
*/
|
|
27
|
+
static validate (work: string, hash: string, threshold?: number): Promise<boolean>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Nano proof-of-work using WebGPU.
|
|
32
|
+
*/
|
|
33
|
+
export declare class NanoPowGpu {
|
|
34
|
+
#private
|
|
35
|
+
static init (): Promise<void>
|
|
36
|
+
static setup (): void
|
|
37
|
+
static reset (): void
|
|
38
|
+
/**
|
|
39
|
+
* Finds a nonce that satisfies the Nano proof-of-work requirements.
|
|
40
|
+
*
|
|
41
|
+
* @param {string} hash - Hexadecimal hash of previous block, or public key for new accounts
|
|
42
|
+
* @param {number} [threshold=0xfffffff8] - Difficulty of proof-of-work calculation
|
|
43
|
+
*/
|
|
44
|
+
static search (hash: string, threshold?: number): Promise<string>
|
|
45
|
+
/**
|
|
46
|
+
* Validates that a nonce satisfies Nano proof-of-work requirements.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} work - Hexadecimal proof-of-work value to validate
|
|
49
|
+
* @param {string} hash - Hexadecimal hash of previous block, or public key for new accounts
|
|
50
|
+
* @param {number} [threshold=0xfffffff8] - Difficulty of proof-of-work calculation
|
|
51
|
+
*/
|
|
52
|
+
static validate (work: string, hash: string, threshold?: number): Promise<boolean>
|
|
53
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nano-pow",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "Proof-of-work generation and validation with WebGPU/WebGL for Nano cryptocurrency.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nemo",
|
|
@@ -40,11 +40,7 @@
|
|
|
40
40
|
"url": "git+https://zoso.dev/nano-pow.git"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
|
-
"build": "rm -rf
|
|
44
|
-
},
|
|
45
|
-
"imports": {
|
|
46
|
-
"#classes": "./src/classes/index.js",
|
|
47
|
-
"#shaders": "./src/shaders/index.js"
|
|
43
|
+
"build": "rm -rf types && tsc && node esbuild.mjs && cp types.d.ts dist"
|
|
48
44
|
},
|
|
49
45
|
"devDependencies": {
|
|
50
46
|
"@types/node": "^22.10.5",
|
|
@@ -55,7 +51,10 @@
|
|
|
55
51
|
"typescript": "^5.7.3"
|
|
56
52
|
},
|
|
57
53
|
"type": "module",
|
|
58
|
-
"exports":
|
|
59
|
-
|
|
54
|
+
"exports": [
|
|
55
|
+
"./dist/main.min.js",
|
|
56
|
+
"./dist/types.d.ts"
|
|
57
|
+
],
|
|
58
|
+
"types": "dist/types.d.ts",
|
|
60
59
|
"unpkg": "dist/main.min.js"
|
|
61
60
|
}
|
package/dist/classes/gl.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export declare class NanoPowGl {
|
|
2
|
-
#private;
|
|
3
|
-
/** Compile */
|
|
4
|
-
static init(): Promise<void>;
|
|
5
|
-
/**
|
|
6
|
-
* Finds a nonce that satisfies the Nano proof-of-work requirements.
|
|
7
|
-
*
|
|
8
|
-
* @param {string} hash - Hexadecimal hash of previous block, or public key for new accounts
|
|
9
|
-
* @param {number} [threshold=0xfffffff8] - Difficulty of proof-of-work calculation
|
|
10
|
-
*/
|
|
11
|
-
static search(hash: string, threshold?: number): Promise<string>;
|
|
12
|
-
/**
|
|
13
|
-
* Validates that a nonce satisfies Nano proof-of-work requirements.
|
|
14
|
-
*
|
|
15
|
-
* @param {string} work - Hexadecimal proof-of-work value to validate
|
|
16
|
-
* @param {string} hash - Hexadecimal hash of previous block, or public key for new accounts
|
|
17
|
-
* @param {number} [threshold=0xfffffff8] - Difficulty of proof-of-work calculation
|
|
18
|
-
*/
|
|
19
|
-
static validate(work: string, hash: string, threshold?: number): Promise<boolean>;
|
|
20
|
-
}
|
package/dist/classes/gl.js
DELETED
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
// SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
|
|
2
|
-
// SPDX-FileContributor: Ben Green <ben@latenightsketches.com>
|
|
3
|
-
// SPDX-License-Identifier: GPL-3.0-or-later AND MIT
|
|
4
|
-
import { NanoPowGlFragmentShader, NanoPowGlVertexShader } from '#shaders';
|
|
5
|
-
export class NanoPowGl {
|
|
6
|
-
/** Used to set canvas size. Must be a multiple of 256. */
|
|
7
|
-
static #WORKLOAD = 256 * Math.max(1, Math.floor(navigator.hardwareConcurrency));
|
|
8
|
-
static #hexify(arr) {
|
|
9
|
-
let out = '';
|
|
10
|
-
for (let i = arr.length - 1; i >= 0; i--) {
|
|
11
|
-
out += arr[i].toString(16).padStart(2, '0');
|
|
12
|
-
}
|
|
13
|
-
return out;
|
|
14
|
-
}
|
|
15
|
-
static #gl;
|
|
16
|
-
static #program;
|
|
17
|
-
static #vertexShader;
|
|
18
|
-
static #fragmentShader;
|
|
19
|
-
static #positionBuffer;
|
|
20
|
-
static #uvBuffer;
|
|
21
|
-
static #uboBuffer;
|
|
22
|
-
static #workBuffer;
|
|
23
|
-
static #query;
|
|
24
|
-
static #pixels;
|
|
25
|
-
/**Vertex Positions, 2 triangles */
|
|
26
|
-
static #positions = new Float32Array([
|
|
27
|
-
-1, -1, 0, -1, 1, 0, 1, 1, 0,
|
|
28
|
-
1, -1, 0, 1, 1, 0, -1, -1, 0
|
|
29
|
-
]);
|
|
30
|
-
/** Texture Positions */
|
|
31
|
-
static #uvPosArray = new Float32Array([
|
|
32
|
-
1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1
|
|
33
|
-
]);
|
|
34
|
-
/** Compile */
|
|
35
|
-
static async init() {
|
|
36
|
-
this.#gl = new OffscreenCanvas(this.#WORKLOAD, this.#WORKLOAD).getContext('webgl2');
|
|
37
|
-
if (this.#gl == null)
|
|
38
|
-
throw new Error('WebGL 2 is required');
|
|
39
|
-
this.#gl.clearColor(0, 0, 0, 1);
|
|
40
|
-
this.#program = this.#gl.createProgram();
|
|
41
|
-
if (this.#program == null)
|
|
42
|
-
throw new Error('Failed to create shader program');
|
|
43
|
-
this.#vertexShader = this.#gl.createShader(this.#gl.VERTEX_SHADER);
|
|
44
|
-
if (this.#vertexShader == null)
|
|
45
|
-
throw new Error('Failed to create vertex shader');
|
|
46
|
-
this.#gl.shaderSource(this.#vertexShader, NanoPowGlVertexShader);
|
|
47
|
-
this.#gl.compileShader(this.#vertexShader);
|
|
48
|
-
if (!this.#gl.getShaderParameter(this.#vertexShader, this.#gl.COMPILE_STATUS))
|
|
49
|
-
throw new Error(this.#gl.getShaderInfoLog(this.#vertexShader) ?? `Failed to compile vertex shader`);
|
|
50
|
-
this.#fragmentShader = this.#gl.createShader(this.#gl.FRAGMENT_SHADER);
|
|
51
|
-
if (this.#fragmentShader == null)
|
|
52
|
-
throw new Error('Failed to create fragment shader');
|
|
53
|
-
this.#gl.shaderSource(this.#fragmentShader, NanoPowGlFragmentShader);
|
|
54
|
-
this.#gl.compileShader(this.#fragmentShader);
|
|
55
|
-
if (!this.#gl.getShaderParameter(this.#fragmentShader, this.#gl.COMPILE_STATUS))
|
|
56
|
-
throw new Error(this.#gl.getShaderInfoLog(this.#fragmentShader) ?? `Failed to compile fragment shader`);
|
|
57
|
-
this.#gl.attachShader(this.#program, this.#vertexShader);
|
|
58
|
-
this.#gl.attachShader(this.#program, this.#fragmentShader);
|
|
59
|
-
this.#gl.linkProgram(this.#program);
|
|
60
|
-
if (!this.#gl.getProgramParameter(this.#program, this.#gl.LINK_STATUS))
|
|
61
|
-
throw new Error(this.#gl.getProgramInfoLog(this.#program) ?? `Failed to link program`);
|
|
62
|
-
/** Construct simple 2D geometry */
|
|
63
|
-
this.#gl.useProgram(this.#program);
|
|
64
|
-
const triangleArray = this.#gl.createVertexArray();
|
|
65
|
-
this.#gl.bindVertexArray(triangleArray);
|
|
66
|
-
this.#positionBuffer = this.#gl.createBuffer();
|
|
67
|
-
this.#gl.bindBuffer(this.#gl.ARRAY_BUFFER, this.#positionBuffer);
|
|
68
|
-
this.#gl.bufferData(this.#gl.ARRAY_BUFFER, this.#positions, this.#gl.STATIC_DRAW);
|
|
69
|
-
this.#gl.vertexAttribPointer(0, 3, this.#gl.FLOAT, false, 0, 0);
|
|
70
|
-
this.#gl.enableVertexAttribArray(0);
|
|
71
|
-
this.#uvBuffer = this.#gl.createBuffer();
|
|
72
|
-
this.#gl.bindBuffer(this.#gl.ARRAY_BUFFER, this.#uvBuffer);
|
|
73
|
-
this.#gl.bufferData(this.#gl.ARRAY_BUFFER, this.#uvPosArray, this.#gl.STATIC_DRAW);
|
|
74
|
-
this.#gl.vertexAttribPointer(1, 2, this.#gl.FLOAT, false, 0, 0);
|
|
75
|
-
this.#gl.enableVertexAttribArray(1);
|
|
76
|
-
this.#uboBuffer = this.#gl.createBuffer();
|
|
77
|
-
this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, this.#uboBuffer);
|
|
78
|
-
this.#gl.bufferData(this.#gl.UNIFORM_BUFFER, 144, this.#gl.DYNAMIC_DRAW);
|
|
79
|
-
this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, null);
|
|
80
|
-
this.#gl.bindBufferBase(this.#gl.UNIFORM_BUFFER, 0, this.#uboBuffer);
|
|
81
|
-
this.#gl.uniformBlockBinding(this.#program, this.#gl.getUniformBlockIndex(this.#program, 'UBO'), 0);
|
|
82
|
-
this.#workBuffer = this.#gl.createBuffer();
|
|
83
|
-
this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, this.#workBuffer);
|
|
84
|
-
this.#gl.bufferData(this.#gl.UNIFORM_BUFFER, 32, this.#gl.STREAM_DRAW);
|
|
85
|
-
this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, null);
|
|
86
|
-
this.#gl.bindBufferBase(this.#gl.UNIFORM_BUFFER, 1, this.#workBuffer);
|
|
87
|
-
this.#gl.uniformBlockBinding(this.#program, this.#gl.getUniformBlockIndex(this.#program, 'WORK'), 1);
|
|
88
|
-
this.#pixels = new Uint8Array(this.#gl.drawingBufferWidth * this.#gl.drawingBufferHeight * 4);
|
|
89
|
-
this.#query = this.#gl.createQuery();
|
|
90
|
-
}
|
|
91
|
-
static #draw(work) {
|
|
92
|
-
if (this.#gl == null || this.#query == null)
|
|
93
|
-
throw new Error('WebGL 2 is required to draw and query pixels');
|
|
94
|
-
if (this.#workBuffer == null)
|
|
95
|
-
throw new Error('Work buffer is required to draw');
|
|
96
|
-
this.#gl.clear(this.#gl.COLOR_BUFFER_BIT);
|
|
97
|
-
/** Upload work buffer */
|
|
98
|
-
this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, this.#workBuffer);
|
|
99
|
-
this.#gl.bufferSubData(this.#gl.UNIFORM_BUFFER, 0, Uint32Array.from(work));
|
|
100
|
-
this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, null);
|
|
101
|
-
this.#gl.beginQuery(this.#gl.ANY_SAMPLES_PASSED_CONSERVATIVE, this.#query);
|
|
102
|
-
this.#gl.drawArrays(this.#gl.TRIANGLES, 0, 6);
|
|
103
|
-
this.#gl.endQuery(this.#gl.ANY_SAMPLES_PASSED_CONSERVATIVE);
|
|
104
|
-
}
|
|
105
|
-
static async #checkQueryResult() {
|
|
106
|
-
if (this.#gl == null || this.#query == null)
|
|
107
|
-
throw new Error('WebGL 2 is required to check query results');
|
|
108
|
-
if (this.#gl.getQueryParameter(this.#query, this.#gl.QUERY_RESULT_AVAILABLE)) {
|
|
109
|
-
return !!(this.#gl.getQueryParameter(this.#query, this.#gl.QUERY_RESULT));
|
|
110
|
-
}
|
|
111
|
-
/** Query result not yet available, check again in the next frame */
|
|
112
|
-
return new Promise((resolve, reject) => {
|
|
113
|
-
try {
|
|
114
|
-
requestAnimationFrame(async () => {
|
|
115
|
-
const result = await NanoPowGl.#checkQueryResult();
|
|
116
|
-
resolve(result);
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
catch (err) {
|
|
120
|
-
reject(err);
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Reads pixels into the work buffer, checks every 4th pixel for the 'found'
|
|
126
|
-
* byte, converts the subsequent 3 pixels with the nonce byte values to a hex
|
|
127
|
-
* string, and returns the result.
|
|
128
|
-
*
|
|
129
|
-
* @param work - Buffer with the original random nonce value
|
|
130
|
-
* @returns Nonce as an 8-byte (16-char) hexadecimal string
|
|
131
|
-
*/
|
|
132
|
-
static #readResult(work) {
|
|
133
|
-
if (this.#gl == null)
|
|
134
|
-
throw new Error('WebGL 2 is required to read pixels');
|
|
135
|
-
this.#gl.readPixels(0, 0, this.#gl.drawingBufferWidth, this.#gl.drawingBufferHeight, this.#gl.RGBA, this.#gl.UNSIGNED_BYTE, this.#pixels);
|
|
136
|
-
for (let i = 0; i < this.#pixels.length; i += 4) {
|
|
137
|
-
if (this.#pixels[i] !== 0) {
|
|
138
|
-
/** Return the work value with the custom bits */
|
|
139
|
-
const hex = this.#hexify(work.subarray(4, 8)) + this.#hexify([
|
|
140
|
-
this.#pixels[i + 2],
|
|
141
|
-
this.#pixels[i + 3],
|
|
142
|
-
work[2] ^ (this.#pixels[i] - 1),
|
|
143
|
-
work[3] ^ (this.#pixels[i + 1] - 1)
|
|
144
|
-
]);
|
|
145
|
-
return hex;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
throw new Error('Query reported result but nonce value not found');
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Finds a nonce that satisfies the Nano proof-of-work requirements.
|
|
152
|
-
*
|
|
153
|
-
* @param {string} hash - Hexadecimal hash of previous block, or public key for new accounts
|
|
154
|
-
* @param {number} [threshold=0xfffffff8] - Difficulty of proof-of-work calculation
|
|
155
|
-
*/
|
|
156
|
-
static async search(hash, threshold = 0xfffffff8) {
|
|
157
|
-
if (NanoPowGl.#gl == null)
|
|
158
|
-
throw new Error('WebGL 2 is required');
|
|
159
|
-
if (!/^[A-Fa-f0-9]{64}$/.test(hash))
|
|
160
|
-
throw new Error(`Invalid hash ${hash}`);
|
|
161
|
-
if (typeof threshold !== 'number')
|
|
162
|
-
throw new TypeError(`Invalid threshold ${threshold}`);
|
|
163
|
-
if (this.#gl == null)
|
|
164
|
-
throw new Error('WebGL 2 is required');
|
|
165
|
-
/** Set up uniform buffer object */
|
|
166
|
-
const uboView = new DataView(new ArrayBuffer(144));
|
|
167
|
-
for (let i = 0; i < 64; i += 8) {
|
|
168
|
-
const uint32 = hash.slice(i, i + 8);
|
|
169
|
-
uboView.setUint32(i * 2, parseInt(uint32, 16));
|
|
170
|
-
}
|
|
171
|
-
uboView.setUint32(128, threshold, true);
|
|
172
|
-
uboView.setFloat32(132, NanoPowGl.#WORKLOAD - 1, true);
|
|
173
|
-
NanoPowGl.#gl.bindBuffer(NanoPowGl.#gl.UNIFORM_BUFFER, NanoPowGl.#uboBuffer);
|
|
174
|
-
NanoPowGl.#gl.bufferSubData(NanoPowGl.#gl.UNIFORM_BUFFER, 0, uboView);
|
|
175
|
-
NanoPowGl.#gl.bindBuffer(NanoPowGl.#gl.UNIFORM_BUFFER, null);
|
|
176
|
-
/** Start drawing to calculate one nonce per pixel */
|
|
177
|
-
let nonce = null;
|
|
178
|
-
const seed = new Uint8Array(8);
|
|
179
|
-
while (nonce == null) {
|
|
180
|
-
crypto.getRandomValues(seed);
|
|
181
|
-
this.#draw(seed);
|
|
182
|
-
const found = await this.#checkQueryResult();
|
|
183
|
-
if (found) {
|
|
184
|
-
nonce = this.#readResult(seed);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
return nonce;
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* Validates that a nonce satisfies Nano proof-of-work requirements.
|
|
191
|
-
*
|
|
192
|
-
* @param {string} work - Hexadecimal proof-of-work value to validate
|
|
193
|
-
* @param {string} hash - Hexadecimal hash of previous block, or public key for new accounts
|
|
194
|
-
* @param {number} [threshold=0xfffffff8] - Difficulty of proof-of-work calculation
|
|
195
|
-
*/
|
|
196
|
-
static async validate(work, hash, threshold = 0xfffffff8) {
|
|
197
|
-
if (NanoPowGl.#gl == null)
|
|
198
|
-
throw new Error('WebGL 2 is required');
|
|
199
|
-
if (!/^[A-Fa-f0-9]{16}$/.test(work))
|
|
200
|
-
throw new Error(`Invalid work ${work}`);
|
|
201
|
-
if (!/^[A-Fa-f0-9]{64}$/.test(hash))
|
|
202
|
-
throw new Error(`Invalid hash ${hash}`);
|
|
203
|
-
if (typeof threshold !== 'number')
|
|
204
|
-
throw new TypeError(`Invalid threshold ${threshold}`);
|
|
205
|
-
if (this.#gl == null)
|
|
206
|
-
throw new Error('WebGL 2 is required');
|
|
207
|
-
/** Set up uniform buffer object */
|
|
208
|
-
const uboView = new DataView(new ArrayBuffer(144));
|
|
209
|
-
for (let i = 0; i < 64; i += 8) {
|
|
210
|
-
const uint32 = hash.slice(i, i + 8);
|
|
211
|
-
uboView.setUint32(i * 2, parseInt(uint32, 16));
|
|
212
|
-
}
|
|
213
|
-
uboView.setUint32(128, threshold, true);
|
|
214
|
-
uboView.setFloat32(132, NanoPowGl.#WORKLOAD - 1, true);
|
|
215
|
-
NanoPowGl.#gl.bindBuffer(NanoPowGl.#gl.UNIFORM_BUFFER, NanoPowGl.#uboBuffer);
|
|
216
|
-
NanoPowGl.#gl.bufferSubData(NanoPowGl.#gl.UNIFORM_BUFFER, 0, uboView);
|
|
217
|
-
NanoPowGl.#gl.bindBuffer(NanoPowGl.#gl.UNIFORM_BUFFER, null);
|
|
218
|
-
/** Start drawing to calculate one nonce per pixel */
|
|
219
|
-
let nonce = null;
|
|
220
|
-
const data = new DataView(new ArrayBuffer(8));
|
|
221
|
-
data.setBigUint64(0, BigInt(`0x${work}`), true);
|
|
222
|
-
const seed = new Uint8Array(data.buffer);
|
|
223
|
-
this.#draw(seed);
|
|
224
|
-
const found = await this.#checkQueryResult();
|
|
225
|
-
if (found) {
|
|
226
|
-
nonce = this.#readResult(seed);
|
|
227
|
-
}
|
|
228
|
-
if (found && nonce !== work)
|
|
229
|
-
throw new Error(`Nonce found but does not match work`);
|
|
230
|
-
return found;
|
|
231
|
-
}
|
|
232
|
-
}
|
package/dist/classes/gpu.d.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Nano proof-of-work using WebGPU.
|
|
3
|
-
*/
|
|
4
|
-
export declare class NanoPowGpu {
|
|
5
|
-
#private;
|
|
6
|
-
static init(): Promise<void>;
|
|
7
|
-
static setup(): void;
|
|
8
|
-
static reset(): void;
|
|
9
|
-
/**
|
|
10
|
-
* Finds a nonce that satisfies the Nano proof-of-work requirements.
|
|
11
|
-
*
|
|
12
|
-
* @param {string} hash - Hexadecimal hash of previous block, or public key for new accounts
|
|
13
|
-
* @param {number} [threshold=0xfffffff8] - Difficulty of proof-of-work calculation
|
|
14
|
-
*/
|
|
15
|
-
static search(hash: string, threshold?: number): Promise<string>;
|
|
16
|
-
/**
|
|
17
|
-
* Validates that a nonce satisfies Nano proof-of-work requirements.
|
|
18
|
-
*
|
|
19
|
-
* @param {string} work - Hexadecimal proof-of-work value to validate
|
|
20
|
-
* @param {string} hash - Hexadecimal hash of previous block, or public key for new accounts
|
|
21
|
-
* @param {number} [threshold=0xfffffff8] - Difficulty of proof-of-work calculation
|
|
22
|
-
*/
|
|
23
|
-
static validate(work: string, hash: string, threshold?: number): Promise<boolean>;
|
|
24
|
-
}
|
package/dist/classes/gpu.js
DELETED
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
// SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
|
|
2
|
-
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
-
/// <reference types="@webgpu/types" />
|
|
4
|
-
import { NanoPowGpuComputeShader } from '#shaders';
|
|
5
|
-
/**
|
|
6
|
-
* Nano proof-of-work using WebGPU.
|
|
7
|
-
*/
|
|
8
|
-
export class NanoPowGpu {
|
|
9
|
-
// Initialize WebGPU
|
|
10
|
-
static #busy = false;
|
|
11
|
-
static #device = null;
|
|
12
|
-
static #uboBuffer;
|
|
13
|
-
static #gpuBuffer;
|
|
14
|
-
static #cpuBuffer;
|
|
15
|
-
static #bindGroupLayout;
|
|
16
|
-
static #pipeline;
|
|
17
|
-
// Initialize WebGPU
|
|
18
|
-
static async init() {
|
|
19
|
-
if (this.#busy)
|
|
20
|
-
return;
|
|
21
|
-
this.#busy = true;
|
|
22
|
-
// Request device and adapter
|
|
23
|
-
if (navigator.gpu == null)
|
|
24
|
-
throw new Error('WebGPU is not supported in this browser.');
|
|
25
|
-
try {
|
|
26
|
-
const adapter = await navigator.gpu.requestAdapter();
|
|
27
|
-
if (adapter == null)
|
|
28
|
-
throw new Error('WebGPU adapter refused by browser.');
|
|
29
|
-
const device = await adapter.requestDevice();
|
|
30
|
-
if (!(device instanceof GPUDevice))
|
|
31
|
-
throw new Error('WebGPU device failed to load.');
|
|
32
|
-
device.lost.then(this.reset);
|
|
33
|
-
this.#device = device;
|
|
34
|
-
this.setup();
|
|
35
|
-
}
|
|
36
|
-
catch (err) {
|
|
37
|
-
throw new Error(`WebGPU initialization failed. ${err}`);
|
|
38
|
-
}
|
|
39
|
-
finally {
|
|
40
|
-
this.#busy = false;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
static setup() {
|
|
44
|
-
if (this.#device == null)
|
|
45
|
-
throw new Error(`WebGPU device failed to load.`);
|
|
46
|
-
// Create buffers for writing GPU calculations and reading from Javascript
|
|
47
|
-
this.#uboBuffer = this.#device.createBuffer({
|
|
48
|
-
size: 48,
|
|
49
|
-
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
50
|
-
});
|
|
51
|
-
this.#gpuBuffer = this.#device.createBuffer({
|
|
52
|
-
size: 16,
|
|
53
|
-
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC
|
|
54
|
-
});
|
|
55
|
-
this.#cpuBuffer = this.#device.createBuffer({
|
|
56
|
-
size: 16,
|
|
57
|
-
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
|
|
58
|
-
});
|
|
59
|
-
// Create binding group data structure and use it later once UBO is known
|
|
60
|
-
this.#bindGroupLayout = this.#device.createBindGroupLayout({
|
|
61
|
-
entries: [
|
|
62
|
-
{
|
|
63
|
-
binding: 0,
|
|
64
|
-
visibility: GPUShaderStage.COMPUTE,
|
|
65
|
-
buffer: { type: 'uniform' },
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
binding: 1,
|
|
69
|
-
visibility: GPUShaderStage.COMPUTE,
|
|
70
|
-
buffer: { type: 'storage' },
|
|
71
|
-
}
|
|
72
|
-
]
|
|
73
|
-
});
|
|
74
|
-
// Create pipeline to connect compute shader to binding layout
|
|
75
|
-
this.#pipeline = this.#device.createComputePipeline({
|
|
76
|
-
layout: this.#device.createPipelineLayout({
|
|
77
|
-
bindGroupLayouts: [this.#bindGroupLayout]
|
|
78
|
-
}),
|
|
79
|
-
compute: {
|
|
80
|
-
entryPoint: 'main',
|
|
81
|
-
module: this.#device.createShaderModule({
|
|
82
|
-
code: NanoPowGpuComputeShader
|
|
83
|
-
})
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
static reset() {
|
|
88
|
-
console.warn(`GPU device lost. Reinitializing...`);
|
|
89
|
-
NanoPowGpu.#cpuBuffer?.destroy();
|
|
90
|
-
NanoPowGpu.#gpuBuffer?.destroy();
|
|
91
|
-
NanoPowGpu.#uboBuffer?.destroy();
|
|
92
|
-
NanoPowGpu.#busy = false;
|
|
93
|
-
NanoPowGpu.init();
|
|
94
|
-
}
|
|
95
|
-
static async #dispatch(seed, hash, threshold, passes) {
|
|
96
|
-
if (this.#device == null)
|
|
97
|
-
throw new Error(`WebGPU device failed to load.`);
|
|
98
|
-
// Set up uniform buffer object
|
|
99
|
-
// Note: u32 size is 4, but total alignment must be multiple of 16
|
|
100
|
-
const uboView = new DataView(new ArrayBuffer(48));
|
|
101
|
-
for (let i = 0; i < 64; i += 8) {
|
|
102
|
-
const uint32 = hash.slice(i, i + 8);
|
|
103
|
-
uboView.setUint32(i / 2, parseInt(uint32, 16));
|
|
104
|
-
}
|
|
105
|
-
uboView.setBigUint64(32, seed, true);
|
|
106
|
-
uboView.setUint32(40, threshold, true);
|
|
107
|
-
this.#device.queue.writeBuffer(this.#uboBuffer, 0, uboView);
|
|
108
|
-
// Reset `nonce` and `found` to 0u in WORK before each calculation
|
|
109
|
-
this.#device.queue.writeBuffer(this.#gpuBuffer, 0, new Uint32Array([0, 0, 0]));
|
|
110
|
-
// Bind UBO read and GPU write buffers
|
|
111
|
-
const bindGroup = this.#device.createBindGroup({
|
|
112
|
-
layout: this.#bindGroupLayout,
|
|
113
|
-
entries: [
|
|
114
|
-
{
|
|
115
|
-
binding: 0,
|
|
116
|
-
resource: {
|
|
117
|
-
buffer: this.#uboBuffer
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
binding: 1,
|
|
122
|
-
resource: {
|
|
123
|
-
buffer: this.#gpuBuffer
|
|
124
|
-
},
|
|
125
|
-
},
|
|
126
|
-
],
|
|
127
|
-
});
|
|
128
|
-
// Create command encoder to issue commands to GPU and initiate computation
|
|
129
|
-
const commandEncoder = this.#device.createCommandEncoder();
|
|
130
|
-
const passEncoder = commandEncoder.beginComputePass();
|
|
131
|
-
// Issue commands and end compute pass structure
|
|
132
|
-
passEncoder.setPipeline(this.#pipeline);
|
|
133
|
-
passEncoder.setBindGroup(0, bindGroup);
|
|
134
|
-
passEncoder.dispatchWorkgroups(passes, passes);
|
|
135
|
-
passEncoder.end();
|
|
136
|
-
// Copy 8-byte nonce and 4-byte found flag from GPU to CPU for reading
|
|
137
|
-
commandEncoder.copyBufferToBuffer(this.#gpuBuffer, 0, this.#cpuBuffer, 0, 12);
|
|
138
|
-
// End computation by passing array of command buffers to command queue for execution
|
|
139
|
-
this.#device.queue.submit([commandEncoder.finish()]);
|
|
140
|
-
// Read results back to Javascript and then unmap buffer after reading
|
|
141
|
-
let data = null;
|
|
142
|
-
try {
|
|
143
|
-
await this.#cpuBuffer.mapAsync(GPUMapMode.READ);
|
|
144
|
-
await this.#device.queue.onSubmittedWorkDone();
|
|
145
|
-
data = new DataView(this.#cpuBuffer.getMappedRange().slice(0));
|
|
146
|
-
this.#cpuBuffer.unmap();
|
|
147
|
-
}
|
|
148
|
-
catch (err) {
|
|
149
|
-
console.warn(`Error getting data from GPU. ${err}`);
|
|
150
|
-
return this.#dispatch(seed, hash, threshold, passes);
|
|
151
|
-
}
|
|
152
|
-
if (data == null)
|
|
153
|
-
throw new Error(`Failed to get data from buffer.`);
|
|
154
|
-
return data;
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Finds a nonce that satisfies the Nano proof-of-work requirements.
|
|
158
|
-
*
|
|
159
|
-
* @param {string} hash - Hexadecimal hash of previous block, or public key for new accounts
|
|
160
|
-
* @param {number} [threshold=0xfffffff8] - Difficulty of proof-of-work calculation
|
|
161
|
-
*/
|
|
162
|
-
static async search(hash, threshold = 0xfffffff8) {
|
|
163
|
-
if (this.#busy) {
|
|
164
|
-
return new Promise(resolve => {
|
|
165
|
-
setTimeout(async () => {
|
|
166
|
-
const result = this.search(hash, threshold);
|
|
167
|
-
resolve(result);
|
|
168
|
-
}, 100);
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
this.#busy = true;
|
|
172
|
-
if (!/^[A-Fa-f0-9]{64}$/.test(hash))
|
|
173
|
-
throw new TypeError(`Invalid hash ${hash}`);
|
|
174
|
-
if (typeof threshold !== 'number')
|
|
175
|
-
throw new TypeError(`Invalid threshold ${threshold}`);
|
|
176
|
-
// Ensure WebGPU is initialized before calculating
|
|
177
|
-
let loads = 0;
|
|
178
|
-
while (this.#device == null && loads < 20) {
|
|
179
|
-
await new Promise(resolve => {
|
|
180
|
-
setTimeout(resolve, 500);
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
if (this.#device == null)
|
|
184
|
-
throw new Error(`WebGPU device failed to load.`);
|
|
185
|
-
let nonce = 0n;
|
|
186
|
-
do {
|
|
187
|
-
const random = Math.floor(Math.random() * 0xffffffff);
|
|
188
|
-
const seed = (BigInt(random) << 32n) | BigInt(random);
|
|
189
|
-
const data = await this.#dispatch(seed, hash, threshold, 0xff);
|
|
190
|
-
nonce = data.getBigUint64(0, true);
|
|
191
|
-
this.#busy = !data.getUint32(8);
|
|
192
|
-
} while (this.#busy);
|
|
193
|
-
return nonce.toString(16).padStart(16, '0');
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Validates that a nonce satisfies Nano proof-of-work requirements.
|
|
197
|
-
*
|
|
198
|
-
* @param {string} work - Hexadecimal proof-of-work value to validate
|
|
199
|
-
* @param {string} hash - Hexadecimal hash of previous block, or public key for new accounts
|
|
200
|
-
* @param {number} [threshold=0xfffffff8] - Difficulty of proof-of-work calculation
|
|
201
|
-
*/
|
|
202
|
-
static async validate(work, hash, threshold = 0xfffffff8) {
|
|
203
|
-
if (this.#busy) {
|
|
204
|
-
return new Promise(resolve => {
|
|
205
|
-
setTimeout(async () => {
|
|
206
|
-
const result = this.validate(work, hash, threshold);
|
|
207
|
-
resolve(result);
|
|
208
|
-
}, 100);
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
this.#busy = true;
|
|
212
|
-
if (!/^[A-Fa-f0-9]{16}$/.test(work))
|
|
213
|
-
throw new TypeError(`Invalid work ${work}`);
|
|
214
|
-
if (!/^[A-Fa-f0-9]{64}$/.test(hash))
|
|
215
|
-
throw new TypeError(`Invalid hash ${hash}`);
|
|
216
|
-
if (typeof threshold !== 'number')
|
|
217
|
-
throw new TypeError(`Invalid threshold ${threshold}`);
|
|
218
|
-
// Ensure WebGPU is initialized before calculating
|
|
219
|
-
let loads = 0;
|
|
220
|
-
while (this.#device == null && loads < 20) {
|
|
221
|
-
await new Promise(resolve => {
|
|
222
|
-
setTimeout(resolve, 500);
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
if (this.#device == null)
|
|
226
|
-
throw new Error(`WebGPU device failed to load.`);
|
|
227
|
-
const seed = BigInt(`0x${work}`);
|
|
228
|
-
const data = await this.#dispatch(seed, hash, threshold, 1);
|
|
229
|
-
const nonce = data.getBigUint64(0, true).toString(16).padStart(16, '0');
|
|
230
|
-
const found = !!data.getUint32(8);
|
|
231
|
-
this.#busy = false;
|
|
232
|
-
if (found && work !== nonce)
|
|
233
|
-
throw new Error(`Nonce found but does not match work`);
|
|
234
|
-
return found;
|
|
235
|
-
}
|
|
236
|
-
}
|
package/dist/classes/index.d.ts
DELETED
package/dist/classes/index.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
// SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
|
|
2
|
-
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
-
import { NanoPowGl } from "./gl.js";
|
|
4
|
-
import { NanoPowGpu } from "./gpu.js";
|
|
5
|
-
let isGlSupported, isGpuSupported = false;
|
|
6
|
-
try {
|
|
7
|
-
await NanoPowGl.init();
|
|
8
|
-
isGlSupported = true;
|
|
9
|
-
}
|
|
10
|
-
catch (err) {
|
|
11
|
-
console.warn(`WebGL is not supported in this environment.`);
|
|
12
|
-
isGlSupported = false;
|
|
13
|
-
}
|
|
14
|
-
try {
|
|
15
|
-
await NanoPowGpu.init();
|
|
16
|
-
isGpuSupported = true;
|
|
17
|
-
}
|
|
18
|
-
catch (err) {
|
|
19
|
-
console.warn(`WebGPU is not supported in this environment.`);
|
|
20
|
-
isGpuSupported = false;
|
|
21
|
-
}
|
|
22
|
-
const NanoPow = isGpuSupported ? NanoPowGpu : isGlSupported ? NanoPowGl : null;
|
|
23
|
-
export { NanoPow, NanoPowGl, NanoPowGpu };
|
package/dist/global.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/global.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
// SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
|
|
2
|
-
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
-
import { NanoPow as np, NanoPowGl as gl, NanoPowGpu as gpu } from './main.js';
|
|
4
|
-
globalThis.NanoPow ??= np;
|
|
5
|
-
globalThis.NanoPowGl ??= gl;
|
|
6
|
-
globalThis.NanoPowGpu ??= gpu;
|
package/dist/main.d.ts
DELETED
package/dist/main.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const NanoPowGlFragmentShader = "#version 300 es\n#pragma vscode_glsllint_stage: frag\nprecision highp float;\nprecision highp int;\n\nin vec2 uv_pos;\nout vec4 fragColor;\n\n// blockhash - array of precalculated block hash components\n// threshold - 0xfffffff8 for send/change blocks, 0xfffffe00 for all else\n// workload - Defines canvas size\nlayout(std140) uniform UBO {\n\tuint blockhash[8];\n\tuint threshold;\n\tfloat workload;\n};\n\n// Random work values\n// First 2 bytes will be overwritten by texture pixel position\n// Second 2 bytes will be modified if the canvas size is greater than 256x256\n// Last 4 bytes remain as generated externally\nlayout(std140) uniform WORK {\n\tuvec4 work[2];\n};\n\n// Defined separately from uint v[32] below as the original value is required\n// to calculate the second uint32 of the digest for threshold comparison\nconst uint BLAKE2B_IV32_1 = 0x6A09E667u;\n\n// Both buffers represent 16 uint64s as 32 uint32s\n// because that's what GLSL offers, just like Javascript\n\n// Compression buffer, intialized to 2 instances of the initialization vector\n// The following values have been modified from the BLAKE2B_IV:\n// OUTLEN is constant 8 bytes\n// v[0] ^= 0x01010000u ^ uint(OUTLEN);\n// INLEN is constant 40 bytes: work value (8) + block hash (32)\n// v[24] ^= uint(INLEN);\n// It's always the \"last\" compression at this INLEN\n// v[28] = ~v[28];\n// v[29] = ~v[29];\nuint v[32] = uint[32](\n\t0xF2BDC900u, 0x6A09E667u, 0x84CAA73Bu, 0xBB67AE85u,\n\t0xFE94F82Bu, 0x3C6EF372u, 0x5F1D36F1u, 0xA54FF53Au,\n\t0xADE682D1u, 0x510E527Fu, 0x2B3E6C1Fu, 0x9B05688Cu,\n\t0xFB41BD6Bu, 0x1F83D9ABu, 0x137E2179u, 0x5BE0CD19u,\n\t0xF3BCC908u, 0x6A09E667u, 0x84CAA73Bu, 0xBB67AE85u,\n\t0xFE94F82Bu, 0x3C6EF372u, 0x5F1D36F1u, 0xA54FF53Au,\n\t0xADE682F9u, 0x510E527Fu, 0x2B3E6C1Fu, 0x9B05688Cu,\n\t0x04BE4294u, 0xE07C2654u, 0x137E2179u, 0x5BE0CD19u\n);\n// Input data buffer\nuint m[32];\n\n// These are offsets into the input data buffer for each mixing step.\n// They are multiplied by 2 from the original SIGMA values in\n// the C reference implementation, which refered to uint64s.\nconst uint SIGMA82[192] = uint[192](\n\t0u,2u,4u,6u,8u,10u,12u,14u,16u,18u,20u,22u,24u,26u,28u,30u,\n\t28u,20u,8u,16u,18u,30u,26u,12u,2u,24u,0u,4u,22u,14u,10u,6u,\n\t22u,16u,24u,0u,10u,4u,30u,26u,20u,28u,6u,12u,14u,2u,18u,8u,\n\t14u,18u,6u,2u,26u,24u,22u,28u,4u,12u,10u,20u,8u,0u,30u,16u,\n\t18u,0u,10u,14u,4u,8u,20u,30u,28u,2u,22u,24u,12u,16u,6u,26u,\n\t4u,24u,12u,20u,0u,22u,16u,6u,8u,26u,14u,10u,30u,28u,2u,18u,\n\t24u,10u,2u,30u,28u,26u,8u,20u,0u,14u,12u,6u,18u,4u,16u,22u,\n\t26u,22u,14u,28u,24u,2u,6u,18u,10u,0u,30u,8u,16u,12u,4u,20u,\n\t12u,30u,28u,18u,22u,6u,0u,16u,24u,4u,26u,14u,2u,8u,20u,10u,\n\t20u,4u,16u,8u,14u,12u,2u,10u,30u,22u,18u,28u,6u,24u,26u,0u,\n\t0u,2u,4u,6u,8u,10u,12u,14u,16u,18u,20u,22u,24u,26u,28u,30u,\n\t28u,20u,8u,16u,18u,30u,26u,12u,2u,24u,0u,4u,22u,14u,10u,6u\n);\n\n// G mixing function\nvoid G (uint ix, uint iy, uint a, uint b, uint c, uint d) {\n\tbool carry;\n\tuint o0;\n\tuint o1;\n\tuint xor0;\n\tuint xor1;\n\n\t// a = a + b;\n\to0 = v[a] + v[b];\n\to1 = v[a+1u] + v[b+1u];\n\tcarry = o0 < v[a];\n\to1 = o1 + uint(mix(0.0, 1.0, float(carry)));\n\tv[a] = o0;\n\tv[a+1u] = o1;\n\n\t// a = a + m[sigma[r][2*i+0]];\n\to0 = v[a] + m[ix];\n\to1 = v[a+1u] + m[ix+1u];\n\tcarry = o0 < v[a];\n\to1 = o1 + uint(mix(0.0, 1.0, float(carry)));\n\tv[a] = o0;\n\tv[a+1u] = o1;\n\n\t// d = rotr64(d ^ a, 32);\n\txor0 = v[d] ^ v[a];\n\txor1 = v[d+1u] ^ v[a+1u];\n\tv[d] = xor1;\n\tv[d+1u] = xor0;\n\n\t// c = c + d;\n\to0 = v[c] + v[d];\n\to1 = v[c+1u] + v[d+1u];\n\tcarry = o0 < v[c];\n\to1 = o1 + uint(mix(0.0, 1.0, float(carry)));\n\tv[c] = o0;\n\tv[c+1u] = o1;\n\n\t// b = rotr64(b ^ c, 24);\n\txor0 = v[b] ^ v[c];\n\txor1 = v[b+1u] ^ v[c+1u];\n\tv[b] = (xor0 >> 24u) ^ (xor1 << 8u);\n\tv[b+1u] = (xor1 >> 24u) ^ (xor0 << 8u);\n\n\t// a = a + b;\n\to0 = v[a] + v[b];\n\to1 = v[a+1u] + v[b+1u];\n\tcarry = o0 < v[a];\n\to1 = o1 + uint(mix(0.0, 1.0, float(carry)));\n\tv[a] = o0;\n\tv[a+1u] = o1;\n\n\t// a = a + m[sigma[r][2*i+1]];\n\to0 = v[a] + m[iy];\n\to1 = v[a+1u] + m[iy+1u];\n\tcarry = o0 < v[a];\n\to1 = o1 + uint(mix(0.0, 1.0, float(carry)));\n\tv[a] = o0;\n\tv[a+1u] = o1;\n\n\t// d = rotr64(d ^ a, 16)\n\txor0 = v[d] ^ v[a];\n\txor1 = v[d+1u] ^ v[a+1u];\n\tv[d] = (xor0 >> 16u) ^ (xor1 << 16u);\n\tv[d+1u] = (xor1 >> 16u) ^ (xor0 << 16u);\n\n\t// c = c + d;\n\to0 = v[c] + v[d];\n\to1 = v[c+1u] + v[d+1u];\n\tcarry = o0 < v[c];\n\to1 = o1 + uint(mix(0.0, 1.0, float(carry)));\n\tv[c] = o0;\n\tv[c+1u] = o1;\n\n\t// b = rotr64(b ^ c, 63)\n\txor0 = v[b] ^ v[c];\n\txor1 = v[b+1u] ^ v[c+1u];\n\tv[b] = (xor1 >> 31u) ^ (xor0 << 1u);\n\tv[b+1u] = (xor0 >> 31u) ^ (xor1 << 1u);\n}\n\nvoid main() {\n\tint i;\n\tuvec4 u_work0 = work[0u];\n\tuvec4 u_work1 = work[1u];\n\tuint uv_x = uint(uv_pos.x * workload);\n\tuint uv_y = uint(uv_pos.y * workload);\n\tuint x_pos = uv_x % 256u;\n\tuint y_pos = uv_y % 256u;\n\tuint x_index = (uv_x - x_pos) / 256u;\n\tuint y_index = (uv_y - y_pos) / 256u;\n\n\t// First 2 work bytes are the x,y pos within the 256x256 area, the next\n\t// two bytes are modified from the random generated value, XOR'd with\n\t// the x,y area index of where this pixel is located\n\tm[0u] = (x_pos ^ (y_pos << 8u) ^ ((u_work0.b ^ x_index) << 16u) ^ ((u_work0.a ^ y_index) << 24u));\n\n\t// Remaining bytes are un-modified from the random generated value\n\tm[1u] = (u_work1.r ^ (u_work1.g << 8u) ^ (u_work1.b << 16u) ^ (u_work1.a << 24u));\n\n\t// Block hash\n\tfor (uint i = 0u; i < 8u; i = i + 1u) {\n\t\tm[i+2u] = blockhash[i];\n\t}\n\n\t// twelve rounds of mixing\n\tfor(uint i = 0u; i < 12u; i = i + 1u) {\n\t\tG(SIGMA82[i * 16u + 0u], SIGMA82[i * 16u + 1u], 0u, 8u, 16u, 24u);\n\t\tG(SIGMA82[i * 16u + 2u], SIGMA82[i * 16u + 3u], 2u, 10u, 18u, 26u);\n\t\tG(SIGMA82[i * 16u + 4u], SIGMA82[i * 16u + 5u], 4u, 12u, 20u, 28u);\n\t\tG(SIGMA82[i * 16u + 6u], SIGMA82[i * 16u + 7u], 6u, 14u, 22u, 30u);\n\t\tG(SIGMA82[i * 16u + 8u], SIGMA82[i * 16u + 9u], 0u, 10u, 20u, 30u);\n\t\tG(SIGMA82[i * 16u + 10u], SIGMA82[i * 16u + 11u], 2u, 12u, 22u, 24u);\n\t\tG(SIGMA82[i * 16u + 12u], SIGMA82[i * 16u + 13u], 4u, 14u, 16u, 26u);\n\t\tG(SIGMA82[i * 16u + 14u], SIGMA82[i * 16u + 15u], 6u, 8u, 18u, 28u);\n\t}\n\n\t// Pixel data is multipled by threshold test result (0 or 1)\n\t// First 4 bytes insignificant, only calculate digest of second 4 bytes\n\tif ((BLAKE2B_IV32_1 ^ v[1u] ^ v[17u]) > threshold) {\n\t\tfragColor = vec4(\n\t\t\tfloat(x_index + 1u)/255.0, // +1 to distinguish from 0 (unsuccessful) pixels\n\t\t\tfloat(y_index + 1u)/255.0, // Same as previous\n\t\t\tfloat(x_pos)/255.0, // Return the 2 custom bytes used in work value\n\t\t\tfloat(y_pos)/255.0 // Second custom byte\n\t\t);\n\t} else {\n \t\tdiscard;\n\t}\n}\n";
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
// SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
|
|
2
|
-
// SPDX-FileContributor: Ben Green <ben@latenightsketches.com>
|
|
3
|
-
// SPDX-License-Identifier: GPL-3.0-or-later AND MIT
|
|
4
|
-
export const NanoPowGlFragmentShader = `#version 300 es
|
|
5
|
-
#pragma vscode_glsllint_stage: frag
|
|
6
|
-
precision highp float;
|
|
7
|
-
precision highp int;
|
|
8
|
-
|
|
9
|
-
in vec2 uv_pos;
|
|
10
|
-
out vec4 fragColor;
|
|
11
|
-
|
|
12
|
-
// blockhash - array of precalculated block hash components
|
|
13
|
-
// threshold - 0xfffffff8 for send/change blocks, 0xfffffe00 for all else
|
|
14
|
-
// workload - Defines canvas size
|
|
15
|
-
layout(std140) uniform UBO {
|
|
16
|
-
uint blockhash[8];
|
|
17
|
-
uint threshold;
|
|
18
|
-
float workload;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
// Random work values
|
|
22
|
-
// First 2 bytes will be overwritten by texture pixel position
|
|
23
|
-
// Second 2 bytes will be modified if the canvas size is greater than 256x256
|
|
24
|
-
// Last 4 bytes remain as generated externally
|
|
25
|
-
layout(std140) uniform WORK {
|
|
26
|
-
uvec4 work[2];
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
// Defined separately from uint v[32] below as the original value is required
|
|
30
|
-
// to calculate the second uint32 of the digest for threshold comparison
|
|
31
|
-
const uint BLAKE2B_IV32_1 = 0x6A09E667u;
|
|
32
|
-
|
|
33
|
-
// Both buffers represent 16 uint64s as 32 uint32s
|
|
34
|
-
// because that's what GLSL offers, just like Javascript
|
|
35
|
-
|
|
36
|
-
// Compression buffer, intialized to 2 instances of the initialization vector
|
|
37
|
-
// The following values have been modified from the BLAKE2B_IV:
|
|
38
|
-
// OUTLEN is constant 8 bytes
|
|
39
|
-
// v[0] ^= 0x01010000u ^ uint(OUTLEN);
|
|
40
|
-
// INLEN is constant 40 bytes: work value (8) + block hash (32)
|
|
41
|
-
// v[24] ^= uint(INLEN);
|
|
42
|
-
// It's always the "last" compression at this INLEN
|
|
43
|
-
// v[28] = ~v[28];
|
|
44
|
-
// v[29] = ~v[29];
|
|
45
|
-
uint v[32] = uint[32](
|
|
46
|
-
0xF2BDC900u, 0x6A09E667u, 0x84CAA73Bu, 0xBB67AE85u,
|
|
47
|
-
0xFE94F82Bu, 0x3C6EF372u, 0x5F1D36F1u, 0xA54FF53Au,
|
|
48
|
-
0xADE682D1u, 0x510E527Fu, 0x2B3E6C1Fu, 0x9B05688Cu,
|
|
49
|
-
0xFB41BD6Bu, 0x1F83D9ABu, 0x137E2179u, 0x5BE0CD19u,
|
|
50
|
-
0xF3BCC908u, 0x6A09E667u, 0x84CAA73Bu, 0xBB67AE85u,
|
|
51
|
-
0xFE94F82Bu, 0x3C6EF372u, 0x5F1D36F1u, 0xA54FF53Au,
|
|
52
|
-
0xADE682F9u, 0x510E527Fu, 0x2B3E6C1Fu, 0x9B05688Cu,
|
|
53
|
-
0x04BE4294u, 0xE07C2654u, 0x137E2179u, 0x5BE0CD19u
|
|
54
|
-
);
|
|
55
|
-
// Input data buffer
|
|
56
|
-
uint m[32];
|
|
57
|
-
|
|
58
|
-
// These are offsets into the input data buffer for each mixing step.
|
|
59
|
-
// They are multiplied by 2 from the original SIGMA values in
|
|
60
|
-
// the C reference implementation, which refered to uint64s.
|
|
61
|
-
const uint SIGMA82[192] = uint[192](
|
|
62
|
-
0u,2u,4u,6u,8u,10u,12u,14u,16u,18u,20u,22u,24u,26u,28u,30u,
|
|
63
|
-
28u,20u,8u,16u,18u,30u,26u,12u,2u,24u,0u,4u,22u,14u,10u,6u,
|
|
64
|
-
22u,16u,24u,0u,10u,4u,30u,26u,20u,28u,6u,12u,14u,2u,18u,8u,
|
|
65
|
-
14u,18u,6u,2u,26u,24u,22u,28u,4u,12u,10u,20u,8u,0u,30u,16u,
|
|
66
|
-
18u,0u,10u,14u,4u,8u,20u,30u,28u,2u,22u,24u,12u,16u,6u,26u,
|
|
67
|
-
4u,24u,12u,20u,0u,22u,16u,6u,8u,26u,14u,10u,30u,28u,2u,18u,
|
|
68
|
-
24u,10u,2u,30u,28u,26u,8u,20u,0u,14u,12u,6u,18u,4u,16u,22u,
|
|
69
|
-
26u,22u,14u,28u,24u,2u,6u,18u,10u,0u,30u,8u,16u,12u,4u,20u,
|
|
70
|
-
12u,30u,28u,18u,22u,6u,0u,16u,24u,4u,26u,14u,2u,8u,20u,10u,
|
|
71
|
-
20u,4u,16u,8u,14u,12u,2u,10u,30u,22u,18u,28u,6u,24u,26u,0u,
|
|
72
|
-
0u,2u,4u,6u,8u,10u,12u,14u,16u,18u,20u,22u,24u,26u,28u,30u,
|
|
73
|
-
28u,20u,8u,16u,18u,30u,26u,12u,2u,24u,0u,4u,22u,14u,10u,6u
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
// G mixing function
|
|
77
|
-
void G (uint ix, uint iy, uint a, uint b, uint c, uint d) {
|
|
78
|
-
bool carry;
|
|
79
|
-
uint o0;
|
|
80
|
-
uint o1;
|
|
81
|
-
uint xor0;
|
|
82
|
-
uint xor1;
|
|
83
|
-
|
|
84
|
-
// a = a + b;
|
|
85
|
-
o0 = v[a] + v[b];
|
|
86
|
-
o1 = v[a+1u] + v[b+1u];
|
|
87
|
-
carry = o0 < v[a];
|
|
88
|
-
o1 = o1 + uint(mix(0.0, 1.0, float(carry)));
|
|
89
|
-
v[a] = o0;
|
|
90
|
-
v[a+1u] = o1;
|
|
91
|
-
|
|
92
|
-
// a = a + m[sigma[r][2*i+0]];
|
|
93
|
-
o0 = v[a] + m[ix];
|
|
94
|
-
o1 = v[a+1u] + m[ix+1u];
|
|
95
|
-
carry = o0 < v[a];
|
|
96
|
-
o1 = o1 + uint(mix(0.0, 1.0, float(carry)));
|
|
97
|
-
v[a] = o0;
|
|
98
|
-
v[a+1u] = o1;
|
|
99
|
-
|
|
100
|
-
// d = rotr64(d ^ a, 32);
|
|
101
|
-
xor0 = v[d] ^ v[a];
|
|
102
|
-
xor1 = v[d+1u] ^ v[a+1u];
|
|
103
|
-
v[d] = xor1;
|
|
104
|
-
v[d+1u] = xor0;
|
|
105
|
-
|
|
106
|
-
// c = c + d;
|
|
107
|
-
o0 = v[c] + v[d];
|
|
108
|
-
o1 = v[c+1u] + v[d+1u];
|
|
109
|
-
carry = o0 < v[c];
|
|
110
|
-
o1 = o1 + uint(mix(0.0, 1.0, float(carry)));
|
|
111
|
-
v[c] = o0;
|
|
112
|
-
v[c+1u] = o1;
|
|
113
|
-
|
|
114
|
-
// b = rotr64(b ^ c, 24);
|
|
115
|
-
xor0 = v[b] ^ v[c];
|
|
116
|
-
xor1 = v[b+1u] ^ v[c+1u];
|
|
117
|
-
v[b] = (xor0 >> 24u) ^ (xor1 << 8u);
|
|
118
|
-
v[b+1u] = (xor1 >> 24u) ^ (xor0 << 8u);
|
|
119
|
-
|
|
120
|
-
// a = a + b;
|
|
121
|
-
o0 = v[a] + v[b];
|
|
122
|
-
o1 = v[a+1u] + v[b+1u];
|
|
123
|
-
carry = o0 < v[a];
|
|
124
|
-
o1 = o1 + uint(mix(0.0, 1.0, float(carry)));
|
|
125
|
-
v[a] = o0;
|
|
126
|
-
v[a+1u] = o1;
|
|
127
|
-
|
|
128
|
-
// a = a + m[sigma[r][2*i+1]];
|
|
129
|
-
o0 = v[a] + m[iy];
|
|
130
|
-
o1 = v[a+1u] + m[iy+1u];
|
|
131
|
-
carry = o0 < v[a];
|
|
132
|
-
o1 = o1 + uint(mix(0.0, 1.0, float(carry)));
|
|
133
|
-
v[a] = o0;
|
|
134
|
-
v[a+1u] = o1;
|
|
135
|
-
|
|
136
|
-
// d = rotr64(d ^ a, 16)
|
|
137
|
-
xor0 = v[d] ^ v[a];
|
|
138
|
-
xor1 = v[d+1u] ^ v[a+1u];
|
|
139
|
-
v[d] = (xor0 >> 16u) ^ (xor1 << 16u);
|
|
140
|
-
v[d+1u] = (xor1 >> 16u) ^ (xor0 << 16u);
|
|
141
|
-
|
|
142
|
-
// c = c + d;
|
|
143
|
-
o0 = v[c] + v[d];
|
|
144
|
-
o1 = v[c+1u] + v[d+1u];
|
|
145
|
-
carry = o0 < v[c];
|
|
146
|
-
o1 = o1 + uint(mix(0.0, 1.0, float(carry)));
|
|
147
|
-
v[c] = o0;
|
|
148
|
-
v[c+1u] = o1;
|
|
149
|
-
|
|
150
|
-
// b = rotr64(b ^ c, 63)
|
|
151
|
-
xor0 = v[b] ^ v[c];
|
|
152
|
-
xor1 = v[b+1u] ^ v[c+1u];
|
|
153
|
-
v[b] = (xor1 >> 31u) ^ (xor0 << 1u);
|
|
154
|
-
v[b+1u] = (xor0 >> 31u) ^ (xor1 << 1u);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
void main() {
|
|
158
|
-
int i;
|
|
159
|
-
uvec4 u_work0 = work[0u];
|
|
160
|
-
uvec4 u_work1 = work[1u];
|
|
161
|
-
uint uv_x = uint(uv_pos.x * workload);
|
|
162
|
-
uint uv_y = uint(uv_pos.y * workload);
|
|
163
|
-
uint x_pos = uv_x % 256u;
|
|
164
|
-
uint y_pos = uv_y % 256u;
|
|
165
|
-
uint x_index = (uv_x - x_pos) / 256u;
|
|
166
|
-
uint y_index = (uv_y - y_pos) / 256u;
|
|
167
|
-
|
|
168
|
-
// First 2 work bytes are the x,y pos within the 256x256 area, the next
|
|
169
|
-
// two bytes are modified from the random generated value, XOR'd with
|
|
170
|
-
// the x,y area index of where this pixel is located
|
|
171
|
-
m[0u] = (x_pos ^ (y_pos << 8u) ^ ((u_work0.b ^ x_index) << 16u) ^ ((u_work0.a ^ y_index) << 24u));
|
|
172
|
-
|
|
173
|
-
// Remaining bytes are un-modified from the random generated value
|
|
174
|
-
m[1u] = (u_work1.r ^ (u_work1.g << 8u) ^ (u_work1.b << 16u) ^ (u_work1.a << 24u));
|
|
175
|
-
|
|
176
|
-
// Block hash
|
|
177
|
-
for (uint i = 0u; i < 8u; i = i + 1u) {
|
|
178
|
-
m[i+2u] = blockhash[i];
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// twelve rounds of mixing
|
|
182
|
-
for(uint i = 0u; i < 12u; i = i + 1u) {
|
|
183
|
-
G(SIGMA82[i * 16u + 0u], SIGMA82[i * 16u + 1u], 0u, 8u, 16u, 24u);
|
|
184
|
-
G(SIGMA82[i * 16u + 2u], SIGMA82[i * 16u + 3u], 2u, 10u, 18u, 26u);
|
|
185
|
-
G(SIGMA82[i * 16u + 4u], SIGMA82[i * 16u + 5u], 4u, 12u, 20u, 28u);
|
|
186
|
-
G(SIGMA82[i * 16u + 6u], SIGMA82[i * 16u + 7u], 6u, 14u, 22u, 30u);
|
|
187
|
-
G(SIGMA82[i * 16u + 8u], SIGMA82[i * 16u + 9u], 0u, 10u, 20u, 30u);
|
|
188
|
-
G(SIGMA82[i * 16u + 10u], SIGMA82[i * 16u + 11u], 2u, 12u, 22u, 24u);
|
|
189
|
-
G(SIGMA82[i * 16u + 12u], SIGMA82[i * 16u + 13u], 4u, 14u, 16u, 26u);
|
|
190
|
-
G(SIGMA82[i * 16u + 14u], SIGMA82[i * 16u + 15u], 6u, 8u, 18u, 28u);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Pixel data is multipled by threshold test result (0 or 1)
|
|
194
|
-
// First 4 bytes insignificant, only calculate digest of second 4 bytes
|
|
195
|
-
if ((BLAKE2B_IV32_1 ^ v[1u] ^ v[17u]) > threshold) {
|
|
196
|
-
fragColor = vec4(
|
|
197
|
-
float(x_index + 1u)/255.0, // +1 to distinguish from 0 (unsuccessful) pixels
|
|
198
|
-
float(y_index + 1u)/255.0, // Same as previous
|
|
199
|
-
float(x_pos)/255.0, // Return the 2 custom bytes used in work value
|
|
200
|
-
float(y_pos)/255.0 // Second custom byte
|
|
201
|
-
);
|
|
202
|
-
} else {
|
|
203
|
-
discard;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
`;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const NanoPowGlVertexShader = "#version 300 es\n#pragma vscode_glsllint_stage: vert\nprecision highp float;\nlayout (location=0) in vec4 position;\nlayout (location=1) in vec2 uv;\n\nout vec2 uv_pos;\n\nvoid main() {\n\tuv_pos = uv;\n\tgl_Position = position;\n}\n";
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
// SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
|
|
2
|
-
// SPDX-FileContributor: Ben Green <ben@latenightsketches.com>
|
|
3
|
-
// SPDX-License-Identifier: GPL-3.0-or-later AND MIT
|
|
4
|
-
export const NanoPowGlVertexShader = `#version 300 es
|
|
5
|
-
#pragma vscode_glsllint_stage: vert
|
|
6
|
-
precision highp float;
|
|
7
|
-
layout (location=0) in vec4 position;
|
|
8
|
-
layout (location=1) in vec2 uv;
|
|
9
|
-
|
|
10
|
-
out vec2 uv_pos;
|
|
11
|
-
|
|
12
|
-
void main() {
|
|
13
|
-
uv_pos = uv;
|
|
14
|
-
gl_Position = position;
|
|
15
|
-
}
|
|
16
|
-
`;
|
package/dist/shaders/index.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { default as NanoPowGpuComputeShader } from "./compute.wgsl";
|
|
2
|
-
import { NanoPowGlFragmentShader } from "./gl-fragment.js";
|
|
3
|
-
import { NanoPowGlVertexShader } from "./gl-vertex.js";
|
|
4
|
-
export { NanoPowGpuComputeShader, NanoPowGlFragmentShader, NanoPowGlVertexShader };
|
package/dist/shaders/index.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
// SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
|
|
2
|
-
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
-
import { default as NanoPowGpuComputeShader } from "./compute.wgsl";
|
|
4
|
-
import { NanoPowGlFragmentShader } from "./gl-fragment.js";
|
|
5
|
-
import { NanoPowGlVertexShader } from "./gl-vertex.js";
|
|
6
|
-
export { NanoPowGpuComputeShader, NanoPowGlFragmentShader, NanoPowGlVertexShader };
|