// Initialize WebGPU
static async init (): Promise<void> {
+ console.log('Initializing NanoPowGpu.')
if (this.#busy) return
this.#busy = true
// Request device and adapter
} catch (err) {
throw new Error('WebGPU initialization failed.', { cause: err })
} finally {
+ this.#isInitialized = true
this.#busy = false
}
- this.#isInitialized = true
}
static async setup (): Promise<void> {
console.table(averages)
}
+ /**
+ * Validate work, if present, and blockhash.
+ * Validate options and normalize its properties.
+ */
+ static async #work_init (work: string | null, hash: string, options?: NanoPowOptions) {
+ if (this.#busy) {
+ console.log('NanoPowGpu is busy. Retrying search...')
+ return new Promise(r => {
+ setTimeout(async () => {
+ r(this.#work_init(work, hash, options))
+ }, 100)
+ }) as Promise<{ difficulty: any, effort: any }>
+ }
+ if (this.#isInitialized === false) this.init()
+ this.#busy = true
+
+ if (!/^[A-Fa-f0-9]{64}$/.test(hash)) throw new TypeError(`Invalid hash ${hash}`)
+ if (work != null && !/^[A-Fa-f0-9]{16}$/.test(work)) throw new TypeError(`Invalid work ${work}`)
+ options ??= {}
+ options.debug ??= false
+ options.difficulty ??= 0xfffffff800000000n
+ options.effort ??= 0x4
+
+ if (typeof options.effort !== 'number'
+ && (options.effort < 0x1 || options.effort > 0x20)
+ ) {
+ throw new TypeError(`Invalid effort ${options.effort}`)
+ }
+ const effort = options.effort
+
+ if (typeof options.difficulty !== 'string'
+ && typeof options.difficulty !== 'bigint'
+ ) {
+ throw new TypeError(`Invalid difficulty ${options.difficulty}`)
+ }
+ const difficulty = typeof options.difficulty === 'string'
+ ? BigInt(`0x${options.difficulty}`)
+ : options.difficulty
+
+ if (difficulty < 0x0n || difficulty > 0xfffffff800000000n) {
+ throw new TypeError(`Invalid difficulty ${options.difficulty}`)
+ }
+
+ this.#debug = !!options.debug
+ if (this.#debug) {
+ console.log('blockhash', hash)
+ console.log(`options`, JSON.stringify(options, (k, v) => typeof v === 'bigint' ? v.toString(16) : v))
+ }
+
+ // Ensure WebGPU is initialized before calculating
+ 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.`)
+ }
+
+ return { difficulty, effort }
+ }
+
static async #dispatch (pipeline: GPUComputePipeline, seed: bigint, hash: string, difficulty: bigint, passes: number): Promise<void> {
if (this.#device == null) throw new Error(`WebGPU device failed to load.`)
// Set up uniform buffer object
* @param {NanoPowOptions} options - Used to configure search execution
*/
static async work_generate (hash: string, options?: NanoPowOptions): Promise<WorkGenerateResponse> {
- 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 (): Promise<void> => {
- const result = this.work_generate(hash, options)
- resolve(result)
- }, 100)
- })
- }
- if (this.#isInitialized === false) this.init()
- this.#busy = true
-
- if (typeof options?.difficulty === 'string') {
- try {
- options.difficulty = BigInt(`0x${options.difficulty}`)
- } catch (err) {
- throw new TypeError(`Invalid difficulty ${options.difficulty}`)
- }
- }
- const difficulty = (typeof options?.difficulty !== 'bigint' || options.difficulty < 0n || options.difficulty > 0xffffffffffffffffn)
- ? 0xfffffff800000000n
- : options.difficulty
- const effort = (typeof options?.effort !== 'number' || options.effort < 0x1 || options.effort > 0x20)
- ? 0x800
- : options.effort * 0x100
- this.#debug = !!(options?.debug)
- if (this.#debug) console.log('NanoPowGpu.work_generate()')
- if (this.#debug) console.log('blockhash', hash)
- if (this.#debug) console.log('search options', JSON.stringify(options, (k, v) => typeof v === 'bigint' ? v.toString(16) : v))
-
- // Ensure WebGPU is initialized before calculating
- 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 { difficulty, effort } = await this.#work_init(null, hash, options)
let times = []
let start = performance.now()
this.#busy = !found
times.push(performance.now() - start)
} while (this.#busy)
- if (this.#debug) this.#logAverages(times)
- if (this.#debug) console.log('nonce', nonce, nonce.toString(16).padStart(16, '0'))
- if (this.#debug) console.log('result', result, result.toString(16).padStart(16, '0'))
+
+ if (this.#debug) {
+ this.#logAverages(times)
+ console.log('nonce', nonce, nonce.toString(16).padStart(16, '0'))
+ console.log('result', result, result.toString(16).padStart(16, '0'))
+ }
+
return {
hash,
work: nonce.toString(16).padStart(16, '0'),
* @param {NanoPowOptions} options - Options used to configure search execution
*/
static async work_validate (work: string, hash: string, options?: NanoPowOptions): Promise<WorkValidateResponse> {
- 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 (): Promise<void> => {
- const result = this.work_validate(work, hash, options)
- resolve(result)
- }, 100)
- })
- }
- if (this.#isInitialized === false) this.init()
- this.#busy = true
-
- if (typeof options?.difficulty === 'string') {
- try {
- options.difficulty = BigInt(`0x${options.difficulty}`)
- } catch (err) {
- throw new TypeError(`Invalid difficulty ${options.difficulty}`)
- }
- }
- const difficulty = (typeof options?.difficulty !== 'bigint' || options.difficulty < 0n || options.difficulty > 0xffffffffffffffffn)
- ? 0xfffffff800000000n
- : options.difficulty
- this.#debug = !!(options?.debug)
- if (this.#debug) console.log('NanoPowGpu.work_validate()')
- if (this.#debug) console.log('blockhash', hash)
- if (this.#debug) console.log('validate options', JSON.stringify(options, (k, v) => typeof v === 'bigint' ? v.toString(16) : v))
-
- // Ensure WebGPU is initialized before calculating
- 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 { difficulty } = await this.#work_init(work, hash, options)
let result = 0n
let nonce = 0n
result = this.#resultView.getBigUint64(16, true)
this.#busy = false
if (seed !== nonce) throw new Error('Result does not match work')
- if (this.#debug) console.log('nonce', nonce, nonce.toString(16).padStart(16, '0'))
- if (this.#debug) console.log('result', result, result.toString(16).padStart(16, '0'))
+
+ if (this.#debug) {
+ console.log('nonce', nonce, nonce.toString(16).padStart(16, '0'))
+ console.log('result', result, result.toString(16).padStart(16, '0'))
+ }
+
const response: WorkValidateResponse = {
hash,
work: nonce.toString(16).padStart(16, '0'),
difficulty: result.toString(16).padStart(16, '0'),
valid_all: (result >= this.#SEND) ? '1' : '0',
- valid_receive: (result >= this.#RECEIVE) ? '1' : '0',
+ valid_receive: (result >= this.#RECEIVE) ? '1' : '0'
+ }
+ if (options?.difficulty != null) {
+ response.valid = (result >= difficulty) ? '1' : '0'
}
- if (options?.difficulty != null) response.valid = (result >= difficulty) ? '1' : '0'
return response
}
}