```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
}
```
```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.
-->
# 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
\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
.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
const options: NanoPowOptions = {
debug: DEBUG,
effort: EFFORT,
- threshold: difficulty
+ difficulty
}
const args = []
if (work) args.push(work)
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;
};
// 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;
}
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
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)
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)
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)
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
}
}
struct UBO {
blockhash: array<vec4<u32>, 2>,
seed: vec2<u32>,
- threshold: vec2<u32>
+ difficulty: vec2<u32>
};
@group(0) @binding(0) var<uniform> ubo: UBO;
****************************************************************************/
/**
- * 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;
console.table(averages)
}
- static async #dispatch (pipeline: GPUComputePipeline, seed: bigint, hash: string, threshold: bigint, passes: number): Promise<DataView> {
+ static async #dispatch (pipeline: GPUComputePipeline, seed: bigint, hash: string, difficulty: bigint, passes: number): Promise<DataView> {
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
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)
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.`)
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
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)
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)
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
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
}
}
*
* @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
}
/**
}
}
- 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')
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 })
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
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 })
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 {
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}<br/>`
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)`
}
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')
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
? '✔️'
})
}
}
- 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))
<p>Times below are in milliseconds and are summarized by various averaging methods.</p>
<p>Level of Effort depends on hardware and does not guarantee faster results.</p>
<hr />
- <label for="threshold" class="hex">Threshold</label>
- <input id="threshold" type="text" value="FFFFFFF800000000" />
+ <label for="difficulty" class="hex">Difficulty</label>
+ <input id="difficulty" type="text" value="FFFFFFF800000000" />
<hr />
<label for="work" class="hex">Validate Work</label>
<input id="work" type="text" />