From: Chris Duncan Date: Fri, 28 Mar 2025 04:27:35 +0000 (-0700) Subject: Complete conversion from threshold to difficulty. X-Git-Tag: v4.0.10~5 X-Git-Url: https://zoso.dev/?a=commitdiff_plain;h=8c158a2eb6a2195bca39e645794c93358639e9eb;p=nano-pow.git Complete conversion from threshold to difficulty. --- diff --git a/README.md b/README.md index ba02a2a..fb13fb6 100644 --- a/README.md +++ b/README.md @@ -76,9 +76,9 @@ const { valid } = await NanoPow.work_validate(work, hash) ```javascript const options = { // default 0xFFFFFFF800000000 for send/change blocks - threshold: number, + difficulty: 'FFFFFFC000000000', // default 8, valid range 1-32 - effort: number, + effort: 16, // default false debug: true } @@ -120,7 +120,7 @@ $ nano-pow --debug 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abc ``` ```console $ # Generate work using customized behavior with options. -$ nano-pow --effort 32 --threshold FFFFFFC000000000 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef +$ nano-pow --effort 32 --difficulty FFFFFFC000000000 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef ``` ```console $ # Validate an existing work nonce against a blockhash. diff --git a/docs/benchmarks.md b/docs/benchmarks.md index e67591b..43c4cbc 100644 --- a/docs/benchmarks.md +++ b/docs/benchmarks.md @@ -4,7 +4,7 @@ SPDX-License-Identifier: GPL-3.0-or-later --> # Benchmarks to compute 16,777,216 nonces -_Each test is 128 samples of one pass (dispatch or frame) at zero threshold with no early exit_ +_Each test is 128 samples of one pass (dispatch or frame) at zero difficulty with no early exit_ ## Summary - Chromium WebGPU and Firefox WebGL are the clear winners diff --git a/docs/nano-pow.1 b/docs/nano-pow.1 index 2c0ff2b..ec4b586 100644 --- a/docs/nano-pow.1 +++ b/docs/nano-pow.1 @@ -37,7 +37,7 @@ Format final output of all hashes as a JSON array of stringified values instead \fB\-e\fR, \fB\-\-effort\fR=\fIEFFORT\fR Increase demand on GPU processing. Must be between 1 and 32 inclusive. .TP -\fB\-t\fR, \fB\-\-threshold\fR=\fITHRESHOLD\fR +\fB\-t\fR, \fB\-\-difficulty\fR=\fIDIFFICULTY\fR Override the minimum threshold value. Higher values increase difficulty. Must be a hexadecimal string between 1 and FFFFFFFFFFFFFFFF inclusive. .TP \fB\-v\fR, \fB\-\-validate\fR=\fIWORK\fR @@ -89,13 +89,13 @@ Validate an existing work value against a blockhash. Requires \fBwork\fR field c .SH EXAMPLES - CLI .PP -Search for a work nonce for a blockhash using the default threshold 0xFFFFFFF800000000: +Search for a work nonce for a blockhash using the default difficulty 0xFFFFFFF800000000: .EX $ nano-pow \fB0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\fR .EE .PP -Search for a work nonce using a custom threshold and increased effort: +Search for a work nonce using a custom difficulty and increased effort: .EX $ nano-pow \fB\-t fffffe0000000000 \-e 32 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\fR .EE diff --git a/src/bin/server.ts b/src/bin/server.ts index 7ae3bb4..9bdaa72 100755 --- a/src/bin/server.ts +++ b/src/bin/server.ts @@ -51,7 +51,7 @@ async function respond (res: http.ServerResponse, data: Buffer[]): Promise const options: NanoPowOptions = { debug: DEBUG, effort: EFFORT, - threshold: difficulty + difficulty } const args = [] if (work) args.push(work) diff --git a/src/lib/gl/gl-draw.frag b/src/lib/gl/gl-draw.frag index bed5396..f58c1a9 100644 --- a/src/lib/gl/gl-draw.frag +++ b/src/lib/gl/gl-draw.frag @@ -13,11 +13,11 @@ precision mediump float; out uvec4 nonce; // blockhash - Array of precalculated block hash components -// threshold - 0xfffffff8 for send/change blocks, 0xfffffe00 for all else +// difficulty - 0xfffffff800000000 for send/change blocks, 0xfffffe0000000000 for all else // validate - If true, checks only 1 pixel to validate, else checks all pixels to search layout(std140) uniform UBO { uint blockhash[8]; - uvec2 threshold; + uvec2 difficulty; bool validate; }; @@ -228,7 +228,7 @@ void main() { // Pixel data set from work seed values uvec2 result = BLAKE2B_INIT[0u] ^ v[0u] ^ v[8u]; - if ((validate && uvec2(gl_FragCoord) == uvec2(0u)) || (result.y > threshold.y || (result.y == threshold.y && result.x > threshold.x))) { + if ((validate && uvec2(gl_FragCoord) == uvec2(0u)) || (result.y > difficulty.y || (result.y == difficulty.y && result.x > difficulty.x))) { nonce = uvec4((BLAKE2B_INIT[0u] ^ v[0u] ^ v[8u]), m[0u]).yxwz; } diff --git a/src/lib/gl/index.ts b/src/lib/gl/index.ts index 0c90c2d..4d638b4 100644 --- a/src/lib/gl/index.ts +++ b/src/lib/gl/index.ts @@ -369,16 +369,16 @@ export class NanoPowGl { if (this.#isInitialized === false) this.init() this.#busy = true - if (typeof options?.threshold === 'string') { + if (typeof options?.difficulty === 'string') { try { - options.threshold = BigInt(`0x${options.threshold}`) + options.difficulty = BigInt(`0x${options.difficulty}`) } catch (err) { - throw new TypeError(`Invalid threshold ${options.threshold}`) + throw new TypeError(`Invalid difficulty ${options.difficulty}`) } } - const threshold = (typeof options?.threshold !== 'bigint' || options.threshold < 1n || options.threshold > 0xffffffffffffffffn) + const difficulty = (typeof options?.difficulty !== 'bigint' || options.difficulty < 1n || options.difficulty > 0xffffffffffffffffn) ? 0xfffffff800000000n - : options.threshold + : options.difficulty const effort = (typeof options?.effort !== 'number' || options.effort < 0x1 || options.effort > 0x20) ? this.#cores : options.effort @@ -409,7 +409,7 @@ export class NanoPowGl { const uint32 = hash.slice(i, i + 8) this.#uboView.setUint32(i * 2, parseInt(uint32, 16)) } - this.#uboView.setBigUint64(128, threshold, true) + this.#uboView.setBigUint64(128, difficulty, true) this.#uboView.setUint32(136, 0, true) if (this.#debug) console.log('UBO', this.#uboView.buffer.slice(0)) this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, this.#uboBuffer) @@ -469,16 +469,16 @@ export class NanoPowGl { if (this.#isInitialized === false) this.init() this.#busy = true - if (typeof options?.threshold === 'string') { + if (typeof options?.difficulty === 'string') { try { - options.threshold = BigInt(`0x${options.threshold}`) + options.difficulty = BigInt(`0x${options.difficulty}`) } catch (err) { - throw new TypeError(`Invalid threshold ${options.threshold}`) + throw new TypeError(`Invalid difficulty ${options.difficulty}`) } } - const threshold = (typeof options?.threshold !== 'bigint' || options.threshold < 1n || options.threshold > 0xffffffffffffffffn) + const difficulty = (typeof options?.difficulty !== 'bigint' || options.difficulty < 1n || options.difficulty > 0xffffffffffffffffn) ? 0xfffffff800000000n - : options.threshold + : options.difficulty this.#debug = !!(options?.debug) if (this.#debug) console.log('NanoPowGl.work_validate()') if (this.#debug) console.log('blockhash', hash) @@ -499,7 +499,7 @@ export class NanoPowGl { const uint32 = hash.slice(i, i + 8) this.#uboView.setUint32(i * 2, parseInt(uint32, 16)) } - this.#uboView.setBigUint64(128, threshold, true) + this.#uboView.setBigUint64(128, difficulty, true) this.#uboView.setUint32(136, 1, true) if (this.#debug) console.log('UBO', this.#uboView.buffer.slice(0)) this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, this.#uboBuffer) @@ -532,7 +532,7 @@ export class NanoPowGl { valid_all: (result >= this.#SEND) ? '1' : '0', valid_receive: (result >= this.#RECEIVE) ? '1' : '0', } - if (options?.threshold != null) response.valid = (result >= threshold) ? '1' : '0' + if (options?.difficulty != null) response.valid = (result >= difficulty) ? '1' : '0' return response } } diff --git a/src/lib/gpu/compute.wgsl b/src/lib/gpu/compute.wgsl index 3b0c7ef..6a0ea13 100644 --- a/src/lib/gpu/compute.wgsl +++ b/src/lib/gpu/compute.wgsl @@ -7,7 +7,7 @@ struct UBO { blockhash: array, 2>, seed: vec2, - threshold: vec2 + difficulty: vec2 }; @group(0) @binding(0) var ubo: UBO; @@ -1659,11 +1659,10 @@ fn main(id: vec3, validate: bool) { ****************************************************************************/ /** - * Set nonce if it passes the threshold and no other thread has set it. - * Only high bits are needed for comparison since threshold low bits are zero. + * Set nonce if it passes the difficulty threshold and no other thread has set it. */ var result = BLAKE2B_INIT[0u] ^ v01.xy ^ v89.xy; - if (validate || ((result.y > ubo.threshold.y || (result.y == ubo.threshold.y && result.y >= ubo.threshold.y)) && atomicLoad(&work.found) == 0u)) { + if (validate || ((result.y > ubo.difficulty.y || (result.y == ubo.difficulty.y && result.y >= ubo.difficulty.y)) && atomicLoad(&work.found) == 0u)) { atomicStore(&work.found, 1u); work.nonce = m0; work.result = result; diff --git a/src/lib/gpu/index.ts b/src/lib/gpu/index.ts index d46eba0..067a168 100644 --- a/src/lib/gpu/index.ts +++ b/src/lib/gpu/index.ts @@ -145,7 +145,7 @@ export class NanoPowGpu { console.table(averages) } - static async #dispatch (pipeline: GPUComputePipeline, seed: bigint, hash: string, threshold: bigint, passes: number): Promise { + static async #dispatch (pipeline: GPUComputePipeline, seed: bigint, hash: string, difficulty: bigint, passes: number): Promise { if (this.#device == null) throw new Error(`WebGPU device failed to load.`) // Set up uniform buffer object // Note: u32 size is 4, but total alignment must be multiple of 16 @@ -155,7 +155,7 @@ export class NanoPowGpu { this.#uboView.setBigUint64(i / 2, BigInt(`0x${u64}`)) } this.#uboView.setBigUint64(32, seed, true) - this.#uboView.setBigUint64(40, threshold, true) + this.#uboView.setBigUint64(40, difficulty, true) if (this.#debug) console.log('UBO', this.#uboView) this.#device.queue.writeBuffer(this.#uboBuffer, 0, this.#uboView) @@ -207,7 +207,7 @@ export class NanoPowGpu { this.#cpuBuffer.unmap() } catch (err) { console.warn(`Error getting data from GPU. ${err}`) - return this.#dispatch(pipeline, seed, hash, threshold, passes) + return this.#dispatch(pipeline, seed, hash, difficulty, passes) } if (this.#debug) console.log('gpuBuffer data', data) if (data == null) throw new Error(`Failed to get data from buffer.`) @@ -234,16 +234,16 @@ export class NanoPowGpu { if (this.#isInitialized === false) this.init() this.#busy = true - if (typeof options?.threshold === 'string') { + if (typeof options?.difficulty === 'string') { try { - options.threshold = BigInt(`0x${options.threshold}`) + options.difficulty = BigInt(`0x${options.difficulty}`) } catch (err) { - throw new TypeError(`Invalid threshold ${options.threshold}`) + throw new TypeError(`Invalid difficulty ${options.difficulty}`) } } - const threshold = (typeof options?.threshold !== 'bigint' || options.threshold < 1n || options.threshold > 0xffffffffffffffffn) + const difficulty = (typeof options?.difficulty !== 'bigint' || options.difficulty < 1n || options.difficulty > 0xffffffffffffffffn) ? 0xfffffff800000000n - : options.threshold + : options.difficulty const effort = (typeof options?.effort !== 'number' || options.effort < 0x1 || options.effort > 0x20) ? 0x800 : options.effort * 0x100 @@ -274,7 +274,7 @@ export class NanoPowGpu { const random1 = Math.floor(Math.random() * 0xffffffff) const seed = (BigInt(random0) << 32n) | BigInt(random1) if (this.#debug) console.log('seed', seed.toString(16).padStart(16, '0')) - const data = await this.#dispatch(this.#searchPipeline, seed, hash, threshold, effort) + const data = await this.#dispatch(this.#searchPipeline, seed, hash, difficulty, effort) const found = !!data.getUint32(0) nonce = data.getBigUint64(8, true) result = data.getBigUint64(16, true) @@ -313,16 +313,16 @@ export class NanoPowGpu { if (this.#isInitialized === false) this.init() this.#busy = true - if (typeof options?.threshold === 'string') { + if (typeof options?.difficulty === 'string') { try { - options.threshold = BigInt(`0x${options.threshold}`) + options.difficulty = BigInt(`0x${options.difficulty}`) } catch (err) { - throw new TypeError(`Invalid threshold ${options.threshold}`) + throw new TypeError(`Invalid difficulty ${options.difficulty}`) } } - const threshold = (typeof options?.threshold !== 'bigint' || options.threshold < 1n || options.threshold > 0xffffffffffffffffn) + const difficulty = (typeof options?.difficulty !== 'bigint' || options.difficulty < 1n || options.difficulty > 0xffffffffffffffffn) ? 0xfffffff800000000n - : options.threshold + : options.difficulty this.#debug = !!(options?.debug) if (this.#debug) console.log('NanoPowGpu.work_validate()') if (this.#debug) console.log('blockhash', hash) @@ -345,7 +345,7 @@ export class NanoPowGpu { const seed = BigInt(`0x${work}`) if (this.#debug) console.log('work', work) - const data = await this.#dispatch(this.#validatePipeline, seed, hash, threshold, 1) + const data = await this.#dispatch(this.#validatePipeline, seed, hash, difficulty, 1) nonce = data.getBigUint64(8, true) result = data.getBigUint64(16, true) this.#busy = false @@ -358,7 +358,7 @@ export class NanoPowGpu { valid_all: (result >= this.#SEND) ? '1' : '0', valid_receive: (result >= this.#RECEIVE) ? '1' : '0', } - if (options?.threshold != null) response.valid = (result >= threshold) ? '1' : '0' + if (options?.difficulty != null) response.valid = (result >= difficulty) ? '1' : '0' return response } } diff --git a/src/types.d.ts b/src/types.d.ts index 2bffd00..2b81a71 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -99,12 +99,12 @@ export type FBO = { * * @param {boolean} [debug=false] - Enables additional debug logging to the console. Default: false * @param {number} [effort=0x8] - Multiplier for dispatching work search. Larger values are not necessarily better since they can quickly overwhelm the GPU. Ignored when validating. Default: 0x8 -* @param {bigint|string} [threshold=0xfffffff800000000] - Minimum value result of `BLAKE2b(nonce||blockhash)`. Default: 0xFFFFFFF800000000 +* @param {bigint|string} [difficulty=0xfffffff800000000] - Minimum value result of `BLAKE2b(nonce||blockhash)`. Default: 0xFFFFFFF800000000 */ export type NanoPowOptions = { debug?: boolean effort?: number - threshold?: bigint | string + difficulty?: bigint | string } /** diff --git a/test/index.html b/test/index.html index 83d05f7..a0a6056 100644 --- a/test/index.html +++ b/test/index.html @@ -69,7 +69,7 @@ SPDX-License-Identifier: GPL-3.0-or-later } } - export async function run (threshold, size, effort, isOutputShown, isGlForced, isDebug) { + export async function run (difficulty, size, effort, isOutputShown, isGlForced, isDebug) { const NP = isGlForced ? NanoPowGl : NanoPow const type = (NP === NanoPowGpu) ? 'WebGPU' : (NP === NanoPowGl) ? 'WebGL' : 'unknown API' console.log(`%cNanoPow`, 'color:green', 'Checking validation against known values') @@ -88,9 +88,9 @@ SPDX-License-Identifier: GPL-3.0-or-later console.log(`work_validate() output for good nonce 2 is ${result === true ? 'correct' : 'incorrect'}`) expect.push(result) - result = await NP.work_validate('326f310d629a8a98', '204076E3364D16A018754FF67D418AB2FBEB38799FF9A29A1D5F9E34F16BEEEA', { threshold: 0xffffffff00000000n, debug: isDebug }) + result = await NP.work_validate('326f310d629a8a98', '204076E3364D16A018754FF67D418AB2FBEB38799FF9A29A1D5F9E34F16BEEEA', { difficulty: 0xffffffff00000000n, debug: isDebug }) result = result.valid === '1' && result.valid_all === '1' && result.valid_receive === '1' - console.log(`work_validate() output for good max threshold nonce is ${result === true ? 'correct' : 'incorrect'}`) + console.log(`work_validate() output for good max difficulty nonce is ${result === true ? 'correct' : 'incorrect'}`) expect.push(result) result = await NP.work_validate('c5d5d6f7c5d6ccd1', '281E89AC73B1082B464B9C3C1168384F846D39F6DF25105F8B4A22915E999117', { debug: isDebug }) @@ -98,9 +98,9 @@ SPDX-License-Identifier: GPL-3.0-or-later console.log(`work_validate() output for colliding nonce is ${result === true ? 'correct' : 'incorrect'}`) expect.push(result) - result = await NP.work_validate('6866c1ac3831a891', '7069D9CD1E85D6204301D254B0927F06ACC794C9EA5DF70EA5578458FB597090', { threshold: 0xfffffe0000000000n, debug: isDebug }) + result = await NP.work_validate('6866c1ac3831a891', '7069D9CD1E85D6204301D254B0927F06ACC794C9EA5DF70EA5578458FB597090', { difficulty: 0xfffffe0000000000n, debug: isDebug }) result = result.valid === '1' && result.valid_all === '0' && result.valid_receive === '1' - console.log(`work_validate() output for good receive threshold nonce is ${result === true ? 'correct' : 'incorrect'}`) + console.log(`work_validate() output for good receive difficulty nonce is ${result === true ? 'correct' : 'incorrect'}`) expect.push(result) // XFAIL @@ -114,9 +114,9 @@ SPDX-License-Identifier: GPL-3.0-or-later console.log(`work_validate() output for bad nonce 2 is ${result === true ? 'correct' : 'incorrect'}`) expect.push(result) - result = await NP.work_validate('ae238556213c3624', 'BF41D87DA3057FDC6050D2B00C06531F89F4AA6195D7C6C2EAAF15B6E703F8F6', { threshold: 0xffffffff00000000n, debug: isDebug }) + result = await NP.work_validate('ae238556213c3624', 'BF41D87DA3057FDC6050D2B00C06531F89F4AA6195D7C6C2EAAF15B6E703F8F6', { difficulty: 0xffffffff00000000n, debug: isDebug }) result = result.valid === '0' && result.valid_all === '0' && result.valid_receive === '1' - console.log(`work_validate() output for bad max threshold nonce is ${result === true ? 'correct' : 'incorrect'}`) + console.log(`work_validate() output for bad max difficulty nonce is ${result === true ? 'correct' : 'incorrect'}`) expect.push(result) result = await NP.work_validate('29a9ae0236990e2e', '32721F4BD2AFB6F6A08D41CD0DF3C0D9C0B5294F68D0D12422F52B28F0800B5F', { debug: isDebug }) @@ -124,14 +124,14 @@ SPDX-License-Identifier: GPL-3.0-or-later console.log(`work_validate() output for slightly wrong nonce is ${result === true ? 'correct' : 'incorrect'}`) expect.push(result) - result = await NP.work_validate('7d903b18d03f9820', '39C57C28F904DFE4012288FFF64CE80C0F42601023A9C82108E8F7B2D186C150', { threshold: 0xfffffe0000000000n, debug: isDebug }) + result = await NP.work_validate('7d903b18d03f9820', '39C57C28F904DFE4012288FFF64CE80C0F42601023A9C82108E8F7B2D186C150', { difficulty: 0xfffffe0000000000n, debug: isDebug }) result = result.valid === '0' && result.valid_all === '0' && result.valid_receive === '0' - console.log(`work_validate() output for bad receive threshold nonce is ${result === true ? 'correct' : 'incorrect'}`) + console.log(`work_validate() output for bad receive difficulty nonce is ${result === true ? 'correct' : 'incorrect'}`) expect.push(result) - result = await NP.work_validate('e45835c3b291c3d1', '9DCD89E2B92FD59D7358C2C2E4C225DF94C88E187B27882F50FEFC3760D3994F', { threshold: 0xffffffff00000000n, debug: isDebug }) + result = await NP.work_validate('e45835c3b291c3d1', '9DCD89E2B92FD59D7358C2C2E4C225DF94C88E187B27882F50FEFC3760D3994F', { difficulty: 0xffffffff00000000n, debug: isDebug }) result = result.valid === '0' && result.valid_all === '1' && result.valid_receive === '1' - console.log(`work_validate() output for send threshold nonce that does not meet custom threshold is ${result === true ? 'correct' : 'incorrect'}`) + console.log(`work_validate() output for send difficulty nonce that does not meet custom difficulty is ${result === true ? 'correct' : 'incorrect'}`) expect.push(result) try { @@ -152,14 +152,14 @@ SPDX-License-Identifier: GPL-3.0-or-later let result = null const start = performance.now() try { - result = await NP.work_generate(hash, { threshold, effort, debug: isDebug }) + result = await NP.work_generate(hash, { difficulty, effort, debug: isDebug }) } catch (err) { document.getElementById('output').innerHTML += `Error: ${err.message}
` console.error(err) return } const end = performance.now() - const check = await NP.work_validate(result.work, result.hash, { threshold, debug: isDebug }) + const check = await NP.work_validate(result.work, result.hash, { difficulty, debug: isDebug }) const isValid = (result.hash === hash && check.valid === '1') ? 'VALID' : 'INVALID' times.push(end - start) const msg = `${isValid} [${result.work}] ${result.hash} (${end - start} ms)` @@ -172,7 +172,7 @@ SPDX-License-Identifier: GPL-3.0-or-later } function startValidation (event) { - const threshold = document.getElementById('threshold') + const difficulty = document.getElementById('difficulty') const isGlForced = document.getElementById('isGlForced') const work = document.getElementById('work') const hash = document.getElementById('hash') @@ -180,7 +180,7 @@ SPDX-License-Identifier: GPL-3.0-or-later validation.innerText = '⏳' if (work.value.length === 16 && hash.value.length === 64) { const NP = isGlForced ? NanoPowGl : NanoPow - NP.work_validate(work.value, hash.value, { threshold: threshold.value }) + NP.work_validate(work.value, hash.value, { difficulty: difficulty.value }) .then(result => { validation.innerText = result ? '✔️' @@ -191,18 +191,18 @@ SPDX-License-Identifier: GPL-3.0-or-later }) } } - document.getElementById('threshold').addEventListener('input', startValidation) + document.getElementById('difficulty').addEventListener('input', startValidation) document.getElementById('work').addEventListener('input', startValidation) document.getElementById('hash').addEventListener('input', startValidation) function startTest (event) { - const threshold = document.getElementById('threshold') + const difficulty = document.getElementById('difficulty') const size = document.getElementById('size') const effort = document.getElementById('effort') const isOutputShown = document.getElementById('isOutputShown') const isGlForced = document.getElementById('isGlForced') const isDebug = document.getElementById('isDebug') - run(threshold.value, +size.value, +effort.value, isOutputShown.checked, isGlForced.checked, isDebug.checked) + run(difficulty.value, +size.value, +effort.value, isOutputShown.checked, isGlForced.checked, isDebug.checked) } document.getElementById('btnStartTest').addEventListener('click', startTest) document.getElementById('effort').value = Math.max(1, Math.floor(navigator.hardwareConcurrency)) @@ -223,8 +223,8 @@ SPDX-License-Identifier: GPL-3.0-or-later

Times below are in milliseconds and are summarized by various averaging methods.

Level of Effort depends on hardware and does not guarantee faster results.


- - + +