import { assert, average, skip, suite, test } from '#GLOBALS.mjs'
import { NANO_TEST_VECTORS } from '#test/TEST_VECTORS.js'
-import { PowGpu, SendBlock } from '#dist/main.js'
+import { PowGl, PowGpu, SendBlock } from '#dist/main.js'
import 'nano-webgl-pow'
await suite('Block performance', async () => {
const COUNT = 0x20
- await skip(`Customized PoW: Time to calculate proof-of-work for a block hash ${COUNT} times`, async () => {
+ await skip(`PowGpu: Time to calculate proof-of-work for a block hash ${COUNT} times`, async () => {
const times = []
const hashes = [
NANO_TEST_VECTORS.PRIVATE_0,
console.log(`Maximum: ${max} ms`)
})
- await test(`Customized PoW: Time to calculate proof-of-work for a send block ${COUNT} times`, async () => {
+ await skip(`PowGl: Time to calculate proof-of-work for a block hash ${COUNT} times`, async () => {
+ const times = []
+ const hashes = [
+ NANO_TEST_VECTORS.PRIVATE_0,
+ NANO_TEST_VECTORS.PRIVATE_1,
+ NANO_TEST_VECTORS.PRIVATE_2,
+ NANO_TEST_VECTORS.PUBLIC_0,
+ NANO_TEST_VECTORS.PUBLIC_1,
+ NANO_TEST_VECTORS.PUBLIC_2
+ ]
+ for (let i = 0; i < 6; i++) {
+ const start = performance.now()
+ const work = await PowGl.find(hashes[i])
+ const end = performance.now()
+ times.push(end - start)
+ console.log(`${work} (${end - start} ms) ${hashes[i]}`)
+ }
+ const { total, arithmetic, harmonic, geometric, min, max } = average(times)
+ console.log(`Total: ${total} ms`)
+ console.log(`Average: ${arithmetic} ms`)
+ console.log(`Harmonic: ${harmonic} ms`)
+ console.log(`Geometric: ${geometric} ms`)
+ console.log(`Minimum: ${min} ms`)
+ console.log(`Maximum: ${max} ms`)
+ })
+
+ await test(`PowGpu: Time to calculate proof-of-work for a send block ${COUNT} times`, async () => {
const times = []
const block = new SendBlock(
NANO_TEST_VECTORS.SEND_BLOCK.account,
console.log(`Maximum: ${max} ms`)
})
- await skip(`Original PoW module: Time to calculate proof-of-work for a send block ${COUNT} times`, async () => {
+ await skip(`nano-webgl-pow: Time to calculate proof-of-work for a send block ${COUNT} times`, async () => {
const times = []
for (let i = 0; i < COUNT; i++) {
const start = performance.now()
import { NanoNaCl } from './workers/nano-nacl.js'
import { Pool } from './pool.js'
import { Rpc } from './rpc.js'
-import { Pow, PowGpu } from './workers.js'
+import { PowGl, PowGpu } from './workers.js'
/**
* Represents a block as defined by the Nano cryptocurrency protocol. The Block
// SPDX-License-Identifier: GPL-3.0-or-later
import { default as Bip44Ckd } from './workers/bip44-ckd.js'
import { default as NanoNaCl } from './workers/nano-nacl.js'
-import { default as Pow } from './workers/powgl.js'
+import { default as PowGl } from './workers/powgl.js'
import { default as PowGpu } from './workers/powgpu.js'
-export { Bip44Ckd, NanoNaCl, Pow, PowGpu }
+export { Bip44Ckd, NanoNaCl, PowGl, PowGpu }
// https://github.com/numtel/nano-webgl-pow
import { WorkerInterface } from '../pool.js'
-export class Pow extends WorkerInterface {
+export class PowGl extends WorkerInterface {
static {
- Pow.listen()
+ PowGl.listen()
}
/**
* Calculates proof-of-work as described by the Nano cryptocurrency protocol.
/**
* Finds a nonce that satisfies the Nano proof-of-work requirements.
*
- * @param {string} hashHex - Hexadecimal hash of previous block, or public key for new accounts
+ * @param {string} hash - Hexadecimal hash of previous block, or public key for new accounts
* @param {number} [threshold=0xfffffff8] - Difficulty of proof-of-work calculation
*/
static async find (hash: string, threshold: number = 0xfffffff8): Promise<string> {
}
static #calculate (hashHex: string, callback: (nonce: string | PromiseLike<string>) => any, threshold: number): void {
- if (Pow.#gl == null) throw new Error('WebGL 2 is required')
+ if (PowGl.#gl == null) throw new Error('WebGL 2 is required')
if (!/^[A-F-a-f0-9]{64}$/.test(hashHex)) throw new Error(`invalid_hash ${hashHex}`)
if (typeof threshold !== 'number') throw new TypeError(`Invalid threshold ${threshold}`)
if (this.#gl == null) throw new Error('WebGL 2 is required')
uboView.setUint32(i * 2, parseInt(uint32, 16))
}
uboView.setUint32(128, threshold, true)
- uboView.setFloat32(132, Pow.#WORKLOAD - 1, true)
- Pow.#gl.bindBuffer(Pow.#gl.UNIFORM_BUFFER, Pow.#uboBuffer)
- Pow.#gl.bufferSubData(Pow.#gl.UNIFORM_BUFFER, 0, uboView)
- Pow.#gl.bindBuffer(Pow.#gl.UNIFORM_BUFFER, null)
+ uboView.setFloat32(132, PowGl.#WORKLOAD - 1, true)
+ PowGl.#gl.bindBuffer(PowGl.#gl.UNIFORM_BUFFER, PowGl.#uboBuffer)
+ PowGl.#gl.bufferSubData(PowGl.#gl.UNIFORM_BUFFER, 0, uboView)
+ PowGl.#gl.bindBuffer(PowGl.#gl.UNIFORM_BUFFER, null)
// Draw output until success or progressCallback says to stop
const work = new Uint8Array(8)
let start: DOMHighResTimeStamp
const draw = (): void => {
start = performance.now()
- if (Pow.#gl == null) throw new Error('WebGL 2 is required')
- if (Pow.#query == null) throw new Error('WebGL 2 is required to run queries')
- Pow.#gl.clear(Pow.#gl.COLOR_BUFFER_BIT)
+ if (PowGl.#gl == null) throw new Error('WebGL 2 is required')
+ if (PowGl.#query == null) throw new Error('WebGL 2 is required to run queries')
+ PowGl.#gl.clear(PowGl.#gl.COLOR_BUFFER_BIT)
// Upload work buffer
crypto.getRandomValues(work)
- Pow.#gl.bindBuffer(Pow.#gl.UNIFORM_BUFFER, Pow.#workBuffer)
- Pow.#gl.bufferSubData(Pow.#gl.UNIFORM_BUFFER, 0, Uint32Array.from(work))
- Pow.#gl.bindBuffer(Pow.#gl.UNIFORM_BUFFER, null)
+ PowGl.#gl.bindBuffer(PowGl.#gl.UNIFORM_BUFFER, PowGl.#workBuffer)
+ PowGl.#gl.bufferSubData(PowGl.#gl.UNIFORM_BUFFER, 0, Uint32Array.from(work))
+ PowGl.#gl.bindBuffer(PowGl.#gl.UNIFORM_BUFFER, null)
- Pow.#gl.beginQuery(Pow.#gl.ANY_SAMPLES_PASSED_CONSERVATIVE, Pow.#query)
- Pow.#gl.drawArrays(Pow.#gl.TRIANGLES, 0, 6)
- Pow.#gl.endQuery(Pow.#gl.ANY_SAMPLES_PASSED_CONSERVATIVE)
+ PowGl.#gl.beginQuery(PowGl.#gl.ANY_SAMPLES_PASSED_CONSERVATIVE, PowGl.#query)
+ PowGl.#gl.drawArrays(PowGl.#gl.TRIANGLES, 0, 6)
+ PowGl.#gl.endQuery(PowGl.#gl.ANY_SAMPLES_PASSED_CONSERVATIVE)
requestAnimationFrame(checkQueryResult)
}
function checkQueryResult () {
- if (Pow.#gl == null) throw new Error('WebGL 2 is required to check query results')
- if (Pow.#query == null) throw new Error('Query not found')
+ if (PowGl.#gl == null) throw new Error('WebGL 2 is required to check query results')
+ if (PowGl.#query == null) throw new Error('Query not found')
console.log(`checking (${performance.now() - start} ms)`)
- if (Pow.#gl.getQueryParameter(Pow.#query, Pow.#gl.QUERY_RESULT_AVAILABLE)) {
+ if (PowGl.#gl.getQueryParameter(PowGl.#query, PowGl.#gl.QUERY_RESULT_AVAILABLE)) {
console.log(`AVAILABLE (${performance.now() - start} ms)`)
- const anySamplesPassed = Pow.#gl.getQueryParameter(Pow.#query, Pow.#gl.QUERY_RESULT)
+ const anySamplesPassed = PowGl.#gl.getQueryParameter(PowGl.#query, PowGl.#gl.QUERY_RESULT)
if (anySamplesPassed) {
// A valid nonce was found
readBackResult()
}
}
function readBackResult () {
- if (Pow.#gl == null) throw new Error('WebGL 2 is required to check read results')
- Pow.#gl.readPixels(0, 0, Pow.#gl.drawingBufferWidth, Pow.#gl.drawingBufferHeight, Pow.#gl.RGBA, Pow.#gl.UNSIGNED_BYTE, Pow.#pixels)
+ if (PowGl.#gl == null) throw new Error('WebGL 2 is required to check read results')
+ PowGl.#gl.readPixels(0, 0, PowGl.#gl.drawingBufferWidth, PowGl.#gl.drawingBufferHeight, PowGl.#gl.RGBA, PowGl.#gl.UNSIGNED_BYTE, PowGl.#pixels)
// Check the pixels for any success
- for (let i = 0; i < Pow.#pixels.length; i += 4) {
- if (Pow.#pixels[i] !== 0) {
+ for (let i = 0; i < PowGl.#pixels.length; i += 4) {
+ if (PowGl.#pixels[i] !== 0) {
console.log(`FOUND (${performance.now() - start} ms)`)
- const hex = Pow.#hexify(work.subarray(4, 8)) + Pow.#hexify([
- Pow.#pixels[i + 2],
- Pow.#pixels[i + 3],
- work[2] ^ (Pow.#pixels[i] - 1),
- work[3] ^ (Pow.#pixels[i + 1] - 1)
+ const hex = PowGl.#hexify(work.subarray(4, 8)) + PowGl.#hexify([
+ PowGl.#pixels[i + 2],
+ PowGl.#pixels[i + 3],
+ work[2] ^ (PowGl.#pixels[i] - 1),
+ work[3] ^ (PowGl.#pixels[i + 1] - 1)
])
// Return the work value with the custom bits
typeof callback === 'function' && callback(hex)
export default `
const WorkerInterface = ${WorkerInterface}
- const Pow = ${Pow}
+ const PowGl = ${PowGl}
`
* @param {string} hash - Hexadecimal hash of previous block, or public key for new accounts
* @param {number} [threshold=0xfffffff8] - Difficulty of proof-of-work calculation
*/
- static async search (hashHex: string, threshold: number = 0xfffffff8): Promise<string> {
- if (!/^[A-Fa-f0-9]{64}$/.test(hashHex)) throw new TypeError(`Invalid hash ${hashHex}`)
+ static async search (hash: string, threshold: number = 0xfffffff8): Promise<string> {
+ if (!/^[A-Fa-f0-9]{64}$/.test(hash)) throw new TypeError(`Invalid hash ${hash}`)
if (typeof threshold !== 'number') throw new TypeError(`Invalid threshold ${threshold}`)
// Ensure WebGPU is initialized before calculating, up to a max time frame
}
return await new Promise(resolve => {
setTimeout(() => {
- resolve(this.search(hashHex, threshold))
+ resolve(this.search(hash, threshold))
}, 100)
})
}
// Note: u32 size is 4, but total alignment must be multiple of 16
const uboView = new DataView(new ArrayBuffer(48))
for (let i = 0; i < 64; i += 8) {
- const uint32 = hashHex.slice(i, i + 8)
+ const uint32 = hash.slice(i, i + 8)
uboView.setUint32(i / 2, parseInt(uint32, 16))
}
const random = Math.floor((Math.random() * 0xffffffff))
const hex = nonce.toString(16).padStart(16, '0')
return hex
} else {
- return await this.search(hashHex, threshold)
+ return await this.search(hash, threshold)
}
}
}
import { Account } from './lib/account.js'
import { Blake2b } from './lib/blake2b.js'
import { SendBlock, ReceiveBlock, ChangeBlock } from './lib/block.js'
-import { Pow } from './lib/workers/powgl.js'
+import { PowGl } from './lib/workers/powgl.js'
import { PowGpu } from './lib/workers/powgpu.js'
import { Rpc } from './lib/rpc.js'
import { Rolodex } from './lib/rolodex.js'
import { Tools } from './lib/tools.js'
import { Bip44Wallet, Blake2bWallet, LedgerWallet } from './lib/wallet.js'
-export { Account, Blake2b, SendBlock, ReceiveBlock, ChangeBlock, Pow, PowGpu, Rpc, Rolodex, Safe, Tools, Bip44Wallet, Blake2bWallet, LedgerWallet }
+export { Account, Blake2b, SendBlock, ReceiveBlock, ChangeBlock, PowGl, PowGpu, Rpc, Rolodex, Safe, Tools, Bip44Wallet, Blake2bWallet, LedgerWallet }