From: Chris Duncan Date: Wed, 8 Jan 2025 22:20:03 +0000 (-0800) Subject: Upload new bundle with inlined G round 0 for platform testing. X-Git-Url: https://zoso.dev/?a=commitdiff_plain;h=9051d9eb996d2c10f686668f9cbf8e13420b418d;p=libnemo.git Upload new bundle with inlined G round 0 for platform testing. --- diff --git a/global.min.js b/global.min.js index 0914734..ebb54ac 100644 --- a/global.min.js +++ b/global.min.js @@ -1199,263 +1199,777 @@ var init_safe = __esm({ } }); -// dist/lib/pool.js -var Pool, WorkerInterface; -var init_pool = __esm({ - "dist/lib/pool.js"() { - "use strict"; - Pool = class _Pool { - static #cores = Math.max(1, navigator.hardwareConcurrency - 1); - #queue = []; - #threads = []; - #url; - get threadsBusy() { - let n = 0; - for (const thread of this.#threads) { - n += +(thread.job != null); - } - return n; - } - get threadsIdle() { - let n = 0; - for (const thread of this.#threads) { - n += +(thread.job == null); - } - return n; - } - async assign(data) { - if (!(data instanceof ArrayBuffer || Array.isArray(data))) - data = [data]; - return new Promise((resolve, reject) => { - const job = { - id: performance.now(), - results: [], - data, - resolve, - reject - }; - if (this.#queue.length > 0) { - this.#queue.push(job); - } else { - for (const thread of this.#threads) - this.#assign(thread, job); - } - }); - } - /** - * - * @param {string} worker - Stringified worker class - * @param {number} [count=1] - Integer between 1 and CPU thread count shared among all Pools - */ - constructor(worker, count = 1) { - count = Math.min(_Pool.#cores, Math.max(1, Math.floor(Math.abs(count)))); - this.#url = URL.createObjectURL(new Blob([worker], { type: "text/javascript" })); - for (let i = 0; i < count; i++) { - const thread = { - worker: new Worker(this.#url, { type: "module" }), - job: null - }; - thread.worker.addEventListener("message", (message) => { - let result = JSON.parse(new TextDecoder().decode(message.data) || "[]"); - if (!Array.isArray(result)) - result = [result]; - this.#report(thread, result); - }); - this.#threads.push(thread); - _Pool.#cores = Math.max(1, _Pool.#cores - this.#threads.length); - } - } - #assign(thread, job) { - if (job.data instanceof ArrayBuffer) { - if (job.data.byteLength > 0) { - thread.job = job; - thread.worker.postMessage({ buffer: job.data }, [job.data]); - } - } else { - const chunk = 1 + job.data.length / this.threadsIdle; - const next = job.data.slice(0, chunk); - job.data = job.data.slice(chunk); - if (job.data.length === 0) - this.#queue.shift(); - if (next?.length > 0) { - const buffer2 = new TextEncoder().encode(JSON.stringify(next)).buffer; - thread.job = job; - thread.worker.postMessage({ buffer: buffer2 }, [buffer2]); - } - } - } - #isJobDone(jobId) { - for (const thread of this.#threads) { - if (thread.job?.id === jobId) - return false; - } - return true; - } - #report(thread, results) { - if (thread.job == null) { - throw new Error("Thread returned results but had nowhere to report it."); - } - const job = thread.job; - if (this.#queue.length > 0) { - this.#assign(thread, this.#queue[0]); - } else { - thread.job = null; - } - if (results.length > 0) { - job.results.push(...results); - } - if (this.#isJobDone(job.id)) { - job.resolve(job.results); - } - } - }; - WorkerInterface = class { - /** - * Processes data through a worker. - * - * Extending classes must override this template by implementing the same - * function signature and providing their own processing call in the try-catch - * block. - * - * @param {any[]} data - Array of data to process - * @returns Promise for that data after being processed - */ - static async work(data) { - return new Promise(async (resolve, reject) => { - for (let d of data) { - try { - d = await d; - } catch (err) { - reject(err); - } - } - resolve(data); - }); - } - /** - * Encodes worker results as an ArrayBuffer so it can be transferred back to - * the main thread. - * - * @param {any[]} results - Array of processed data - */ - static report(results) { - const buffer2 = new TextEncoder().encode(JSON.stringify(results)).buffer; - postMessage(buffer2, [buffer2]); - } - /** - * Listens for messages from the main thread. - * - * Extending classes must call this in a static initialization block: - * ``` - * static { - * Pow.listen() - * } - * ``` - */ - static listen() { - addEventListener("message", (message) => { - const { name, buffer: buffer2 } = message.data; - if (name === "STOP") { - close(); - const buffer3 = new ArrayBuffer(0); - postMessage(buffer3, [buffer3]); - } else { - const data = JSON.parse(new TextDecoder().decode(buffer2)); - this.work(data).then(this.report); - } - }); - } - }; - } -}); - -// dist/lib/workers/nano-nacl.js -var NanoNaCl, nano_nacl_default; -var init_nano_nacl = __esm({ - "dist/lib/workers/nano-nacl.js"() { +// src/lib/blake2b.ts +var Blake2b2, blake2b_default2; +var init_blake2b2 = __esm({ + "src/lib/blake2b.ts"() { "use strict"; - init_blake2b(); - init_pool(); - NanoNaCl = class _NanoNaCl extends WorkerInterface { - static { - _NanoNaCl.listen(); - } - static async work(data) { - return new Promise(async (resolve, reject) => { - for (let d of data) { - try { - d.publicKey = await this.convert(d.privateKey); - } catch (err) { - reject(err); - } - } - resolve(data); - }); - } - static gf = function(init) { - const r = new Float64Array(16); - if (init) - for (let i = 0; i < init.length; i++) - r[i] = init[i]; - return r; - }; - static gf0 = this.gf(); - static gf1 = this.gf([1]); - static D = this.gf([30883, 4953, 19914, 30187, 55467, 16705, 2637, 112, 59544, 30585, 16505, 36039, 65139, 11119, 27886, 20995]); - static D2 = this.gf([61785, 9906, 39828, 60374, 45398, 33411, 5274, 224, 53552, 61171, 33010, 6542, 64743, 22239, 55772, 9222]); - static X = this.gf([54554, 36645, 11616, 51542, 42930, 38181, 51040, 26924, 56412, 64982, 57905, 49316, 21502, 52590, 14035, 8553]); - static Y = this.gf([26200, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214]); - static I = this.gf([41136, 18958, 6951, 50414, 58488, 44335, 6150, 12099, 55207, 15867, 153, 11085, 57099, 20417, 9344, 11139]); - static vn(x, xi, y, yi, n) { - let d = 0; - for (let i = 0; i < n; i++) - d |= x[xi + i] ^ y[yi + i]; - return (1 & d - 1 >>> 8) - 1; - } - static crypto_verify_32(x, xi, y, yi) { - return this.vn(x, xi, y, yi, 32); - } - static set25519(r, a) { - for (let i = 0; i < 16; i++) - r[i] = a[i] | 0; - } - static car25519(o) { - let v, c = 1; - for (let i = 0; i < 16; i++) { - v = o[i] + c + 65535; - c = Math.floor(v / 65536); - o[i] = v - c * 65536; - } - o[0] += c - 1 + 37 * (c - 1); - } - static sel25519(p, q, b) { - let t; - const c = ~(b - 1); - for (let i = 0; i < 16; i++) { - t = c & (p[i] ^ q[i]); - p[i] ^= t; - q[i] ^= t; - } - } - static pack25519(o, n) { - let b; - const m = this.gf(); - const t = this.gf(); - for (let i = 0; i < 16; i++) - t[i] = n[i]; - this.car25519(t); - this.car25519(t); - this.car25519(t); - for (let j = 0; j < 2; j++) { - m[0] = t[0] - 65517; - for (let i = 1; i < 15; i++) { - m[i] = t[i] - 65535 - (m[i - 1] >> 16 & 1); - m[i - 1] &= 65535; - } - m[15] = t[15] - 32767 - (m[14] >> 16 & 1); - b = m[15] >> 16 & 1; - m[14] &= 65535; - this.sel25519(t, m, 1 - b); + Blake2b2 = class _Blake2b { + static BYTES_MIN = 1; + static BYTES_MAX = 64; + static KEYBYTES_MIN = 16; + static KEYBYTES_MAX = 64; + static SALTBYTES = 16; + static PERSONALBYTES = 16; + // Initialization Vector + static BLAKE2B_IV32 = new Uint32Array([ + 4089235720, + 1779033703, + 2227873595, + 3144134277, + 4271175723, + 1013904242, + 1595750129, + 2773480762, + 2917565137, + 1359893119, + 725511199, + 2600822924, + 4215389547, + 528734635, + 327033209, + 1541459225 + ]); + static SIGMA8 = [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 14, + 10, + 4, + 8, + 9, + 15, + 13, + 6, + 1, + 12, + 0, + 2, + 11, + 7, + 5, + 3, + 11, + 8, + 12, + 0, + 5, + 2, + 15, + 13, + 10, + 14, + 3, + 6, + 7, + 1, + 9, + 4, + 7, + 9, + 3, + 1, + 13, + 12, + 11, + 14, + 2, + 6, + 5, + 10, + 4, + 0, + 15, + 8, + 9, + 0, + 5, + 7, + 2, + 4, + 10, + 15, + 14, + 1, + 11, + 12, + 6, + 8, + 3, + 13, + 2, + 12, + 6, + 10, + 0, + 11, + 8, + 3, + 4, + 13, + 7, + 5, + 15, + 14, + 1, + 9, + 12, + 5, + 1, + 15, + 14, + 13, + 4, + 10, + 0, + 7, + 6, + 3, + 9, + 2, + 8, + 11, + 13, + 11, + 7, + 14, + 12, + 1, + 3, + 9, + 5, + 0, + 15, + 4, + 8, + 6, + 2, + 10, + 6, + 15, + 14, + 9, + 11, + 3, + 0, + 8, + 12, + 2, + 13, + 7, + 1, + 4, + 10, + 5, + 10, + 2, + 8, + 4, + 7, + 6, + 1, + 5, + 15, + 11, + 9, + 14, + 3, + 12, + 13, + 0, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 14, + 10, + 4, + 8, + 9, + 15, + 13, + 6, + 1, + 12, + 0, + 2, + 11, + 7, + 5, + 3 + ]; + /** + * These are offsets into a uint64 buffer. + * Multiply them all by 2 to make them offsets into a uint32 buffer, + * because this is Javascript and we don't have uint64s + */ + static SIGMA82 = new Uint8Array(this.SIGMA8.map((x) => x * 2)); + // reusable parameter_block + static parameter_block = new Uint8Array([ + 0, + 0, + 0, + 0, + // 0: outlen, keylen, fanout, depth + 0, + 0, + 0, + 0, + // 4: leaf length, sequential mode + 0, + 0, + 0, + 0, + // 8: node offset + 0, + 0, + 0, + 0, + // 12: node offset + 0, + 0, + 0, + 0, + // 16: node depth, inner length, rfu + 0, + 0, + 0, + 0, + // 20: rfu + 0, + 0, + 0, + 0, + // 24: rfu + 0, + 0, + 0, + 0, + // 28: rfu + 0, + 0, + 0, + 0, + // 32: salt + 0, + 0, + 0, + 0, + // 36: salt + 0, + 0, + 0, + 0, + // 40: salt + 0, + 0, + 0, + 0, + // 44: salt + 0, + 0, + 0, + 0, + // 48: personal + 0, + 0, + 0, + 0, + // 52: personal + 0, + 0, + 0, + 0, + // 56: personal + 0, + 0, + 0, + 0 + // 60: personal + ]); + static v = new Uint32Array(32); + static m = new Uint32Array(32); + /** + * 64-bit unsigned addition + * Sets v[a,a+1] += v[b,b+1] + * v should be a Uint32Array + */ + static ADD64AA(v, a, b) { + var o0 = v[a] + v[b]; + var o1 = v[a + 1] + v[b + 1]; + if (o0 >= 4294967296) { + o1++; + } + v[a] = o0; + v[a + 1] = o1; + } + /** + * 64-bit unsigned addition + * Sets v[a,a+1] += b + * b0 is the low 32 bits of b, b1 represents the high 32 bits + */ + static ADD64AC(v, a, b0, b1) { + var o0 = v[a] + b0; + if (b0 < 0) { + o0 += 4294967296; + } + var o1 = v[a + 1] + b1; + if (o0 >= 4294967296) { + o1++; + } + v[a] = o0; + v[a + 1] = o1; + } + // Little-endian byte access + static B2B_GET32(arr, i) { + return arr[i] ^ arr[i + 1] << 8 ^ arr[i + 2] << 16 ^ arr[i + 3] << 24; + } + /** + * G Mixing function + * The ROTRs are inlined for speed + */ + static B2B_G(a, b, c, d, ix, iy) { + var x0 = _Blake2b.m[ix]; + var x1 = _Blake2b.m[ix + 1]; + var y0 = _Blake2b.m[iy]; + var y1 = _Blake2b.m[iy + 1]; + _Blake2b.ADD64AA(_Blake2b.v, a, b); + _Blake2b.ADD64AC(_Blake2b.v, a, x0, x1); + var xor0 = _Blake2b.v[d] ^ _Blake2b.v[a]; + var xor1 = _Blake2b.v[d + 1] ^ _Blake2b.v[a + 1]; + _Blake2b.v[d] = xor1; + _Blake2b.v[d + 1] = xor0; + _Blake2b.ADD64AA(_Blake2b.v, c, d); + xor0 = _Blake2b.v[b] ^ _Blake2b.v[c]; + xor1 = _Blake2b.v[b + 1] ^ _Blake2b.v[c + 1]; + _Blake2b.v[b] = xor0 >>> 24 ^ xor1 << 8; + _Blake2b.v[b + 1] = xor1 >>> 24 ^ xor0 << 8; + _Blake2b.ADD64AA(_Blake2b.v, a, b); + _Blake2b.ADD64AC(_Blake2b.v, a, y0, y1); + xor0 = _Blake2b.v[d] ^ _Blake2b.v[a]; + xor1 = _Blake2b.v[d + 1] ^ _Blake2b.v[a + 1]; + _Blake2b.v[d] = xor0 >>> 16 ^ xor1 << 16; + _Blake2b.v[d + 1] = xor1 >>> 16 ^ xor0 << 16; + _Blake2b.ADD64AA(_Blake2b.v, c, d); + xor0 = _Blake2b.v[b] ^ _Blake2b.v[c]; + xor1 = _Blake2b.v[b + 1] ^ _Blake2b.v[c + 1]; + _Blake2b.v[b] = xor1 >>> 31 ^ xor0 << 1; + _Blake2b.v[b + 1] = xor0 >>> 31 ^ xor1 << 1; + } + /** + * Compression function. 'last' flag indicates last block. + * Note we're representing 16 uint64s as 32 uint32s + */ + static blake2bCompress(ctx, last2) { + let i = 0; + for (i = 0; i < 16; i++) { + _Blake2b.v[i] = ctx.h[i]; + _Blake2b.v[i + 16] = _Blake2b.BLAKE2B_IV32[i]; + } + _Blake2b.v[24] = _Blake2b.v[24] ^ ctx.t; + _Blake2b.v[25] = _Blake2b.v[25] ^ ctx.t / 4294967296; + if (last2) { + _Blake2b.v[28] = ~_Blake2b.v[28]; + _Blake2b.v[29] = ~_Blake2b.v[29]; + } + for (i = 0; i < 32; i++) { + _Blake2b.m[i] = _Blake2b.B2B_GET32(ctx.b, 4 * i); + } + for (i = 0; i < 12; i++) { + _Blake2b.B2B_G(0, 8, 16, 24, _Blake2b.SIGMA82[i * 16 + 0], _Blake2b.SIGMA82[i * 16 + 1]); + _Blake2b.B2B_G(2, 10, 18, 26, _Blake2b.SIGMA82[i * 16 + 2], _Blake2b.SIGMA82[i * 16 + 3]); + _Blake2b.B2B_G(4, 12, 20, 28, _Blake2b.SIGMA82[i * 16 + 4], _Blake2b.SIGMA82[i * 16 + 5]); + _Blake2b.B2B_G(6, 14, 22, 30, _Blake2b.SIGMA82[i * 16 + 6], _Blake2b.SIGMA82[i * 16 + 7]); + _Blake2b.B2B_G(0, 10, 20, 30, _Blake2b.SIGMA82[i * 16 + 8], _Blake2b.SIGMA82[i * 16 + 9]); + _Blake2b.B2B_G(2, 12, 22, 24, _Blake2b.SIGMA82[i * 16 + 10], _Blake2b.SIGMA82[i * 16 + 11]); + _Blake2b.B2B_G(4, 14, 16, 26, _Blake2b.SIGMA82[i * 16 + 12], _Blake2b.SIGMA82[i * 16 + 13]); + _Blake2b.B2B_G(6, 8, 18, 28, _Blake2b.SIGMA82[i * 16 + 14], _Blake2b.SIGMA82[i * 16 + 15]); + } + for (i = 0; i < 16; i++) { + ctx.h[i] = ctx.h[i] ^ _Blake2b.v[i] ^ _Blake2b.v[i + 16]; + } + } + /** + * Updates a BLAKE2b streaming hash + * Requires hash context and Uint8Array (byte array) + */ + static blake2bUpdate(ctx, input) { + for (var i = 0; i < input.length; i++) { + if (ctx.c === 128) { + ctx.t += ctx.c; + _Blake2b.blake2bCompress(ctx, false); + ctx.c = 0; + } + ctx.b[ctx.c++] = input[i]; + } + } + /** + * Completes a BLAKE2b streaming hash + * Returns a Uint8Array containing the message digest + */ + static blake2bFinal(ctx, out) { + ctx.t += ctx.c; + while (ctx.c < 128) { + ctx.b[ctx.c++] = 0; + } + _Blake2b.blake2bCompress(ctx, true); + for (var i = 0; i < ctx.outlen; i++) { + out[i] = ctx.h[i >> 2] >> 8 * (i & 3); + } + return out; + } + static hexSlice(buf) { + let str = ""; + for (let i = 0; i < buf.length; i++) str += _Blake2b.toHex(buf[i]); + return str; + } + static toHex(n) { + if (typeof n !== "number") + throw new TypeError(`expected number to convert to hex; received ${typeof n}`); + if (n < 0 || n > 255) + throw new RangeError(`expected byte value 0-255; received ${n}`); + return n.toString(16).padStart(2, "0"); + } + b; + h; + t; + c; + outlen; + /** + * Creates a BLAKE2b hashing context + * Requires an output length between 1 and 64 bytes + * Takes an optional Uint8Array key + */ + constructor(outlen, key, salt, personal, noAssert) { + if (noAssert !== true) { + if (outlen < _Blake2b.BYTES_MIN) throw new RangeError(`expected outlen >= ${_Blake2b.BYTES_MIN}; actual ${outlen}`); + if (outlen > _Blake2b.BYTES_MAX) throw new RangeError(`expectd outlen <= ${_Blake2b.BYTES_MAX}; actual ${outlen}`); + if (key != null) { + if (!(key instanceof Uint8Array)) throw new TypeError(`key must be Uint8Array or Buffer`); + if (key.length < _Blake2b.KEYBYTES_MIN) throw new RangeError(`expected key >= ${_Blake2b.KEYBYTES_MIN}; actual ${key.length}`); + if (key.length > _Blake2b.KEYBYTES_MAX) throw new RangeError(`expected key <= ${_Blake2b.KEYBYTES_MAX}; actual ${key.length}`); + } + if (salt != null) { + if (!(salt instanceof Uint8Array)) throw new TypeError(`salt must be Uint8Array or Buffer`); + if (salt.length !== _Blake2b.SALTBYTES) throw new RangeError(`expected salt ${_Blake2b.SALTBYTES} bytes; actual ${salt.length} bytes`); + } + if (personal != null) { + if (!(personal instanceof Uint8Array)) throw new TypeError(`personal must be Uint8Array or Buffer`); + if (personal.length !== _Blake2b.PERSONALBYTES) throw new RangeError(`expected personal ${_Blake2b.PERSONALBYTES} bytes; actual ${personal.length} bytes`); + } + } + this.b = new Uint8Array(128); + this.h = new Uint32Array(16); + this.t = 0; + this.c = 0; + this.outlen = outlen; + _Blake2b.parameter_block.fill(0); + _Blake2b.parameter_block[0] = outlen; + if (key) _Blake2b.parameter_block[1] = key.length; + _Blake2b.parameter_block[2] = 1; + _Blake2b.parameter_block[3] = 1; + if (salt) _Blake2b.parameter_block.set(salt, 32); + if (personal) _Blake2b.parameter_block.set(personal, 48); + for (var i = 0; i < 16; i++) { + this.h[i] = _Blake2b.BLAKE2B_IV32[i] ^ _Blake2b.B2B_GET32(_Blake2b.parameter_block, i * 4); + } + if (key) { + _Blake2b.blake2bUpdate(this, key); + this.c = 128; + } + } + update(input) { + if (!(input instanceof Uint8Array)) + throw new TypeError(`input must be Uint8Array or Buffer`); + _Blake2b.blake2bUpdate(this, input); + return this; + } + digest(out) { + const buf = !out || out === "binary" || out === "hex" ? new Uint8Array(this.outlen) : out; + if (!(buf instanceof Uint8Array)) throw new TypeError(`out must be "binary", "hex", Uint8Array, or Buffer`); + if (buf.length < this.outlen) throw new RangeError(`out must have at least outlen bytes of space`); + _Blake2b.blake2bFinal(this, buf); + if (out === "hex") return _Blake2b.hexSlice(buf); + return buf; + } + }; + blake2b_default2 = Blake2b2.toString(); + } +}); + +// src/lib/pool.ts +var Pool, WorkerInterface; +var init_pool = __esm({ + "src/lib/pool.ts"() { + "use strict"; + Pool = class _Pool { + static #cores = Math.max(1, navigator.hardwareConcurrency - 1); + #queue = []; + #threads = []; + #url; + get threadsBusy() { + let n = 0; + for (const thread of this.#threads) { + n += +(thread.job != null); + } + return n; + } + get threadsIdle() { + let n = 0; + for (const thread of this.#threads) { + n += +(thread.job == null); + } + return n; + } + async assign(data) { + if (!(data instanceof ArrayBuffer || Array.isArray(data))) data = [data]; + return new Promise((resolve, reject) => { + const job = { + id: performance.now(), + results: [], + data, + resolve, + reject + }; + if (this.#queue.length > 0) { + this.#queue.push(job); + } else { + for (const thread of this.#threads) this.#assign(thread, job); + } + }); + } + /** + * + * @param {string} worker - Stringified worker class + * @param {number} [count=1] - Integer between 1 and CPU thread count shared among all Pools + */ + constructor(worker, count = 1) { + count = Math.min(_Pool.#cores, Math.max(1, Math.floor(Math.abs(count)))); + this.#url = URL.createObjectURL(new Blob([worker], { type: "text/javascript" })); + for (let i = 0; i < count; i++) { + const thread = { + worker: new Worker(this.#url, { type: "module" }), + job: null + }; + thread.worker.addEventListener("message", (message) => { + let result = JSON.parse(new TextDecoder().decode(message.data) || "[]"); + if (!Array.isArray(result)) result = [result]; + this.#report(thread, result); + }); + this.#threads.push(thread); + _Pool.#cores = Math.max(1, _Pool.#cores - this.#threads.length); + } + } + #assign(thread, job) { + if (job.data instanceof ArrayBuffer) { + if (job.data.byteLength > 0) { + thread.job = job; + thread.worker.postMessage({ buffer: job.data }, [job.data]); + } + } else { + const chunk = 1 + job.data.length / this.threadsIdle; + const next = job.data.slice(0, chunk); + job.data = job.data.slice(chunk); + if (job.data.length === 0) this.#queue.shift(); + if (next?.length > 0) { + const buffer2 = new TextEncoder().encode(JSON.stringify(next)).buffer; + thread.job = job; + thread.worker.postMessage({ buffer: buffer2 }, [buffer2]); + } + } + } + #isJobDone(jobId) { + for (const thread of this.#threads) { + if (thread.job?.id === jobId) return false; + } + return true; + } + #report(thread, results) { + if (thread.job == null) { + throw new Error("Thread returned results but had nowhere to report it."); + } + const job = thread.job; + if (this.#queue.length > 0) { + this.#assign(thread, this.#queue[0]); + } else { + thread.job = null; + } + if (results.length > 0) { + job.results.push(...results); + } + if (this.#isJobDone(job.id)) { + job.resolve(job.results); + } + } + }; + WorkerInterface = class { + /** + * Processes data through a worker. + * + * Extending classes must override this template by implementing the same + * function signature and providing their own processing call in the try-catch + * block. + * + * @param {any[]} data - Array of data to process + * @returns Promise for that data after being processed + */ + static async work(data) { + return new Promise(async (resolve, reject) => { + for (let d of data) { + try { + d = await d; + } catch (err) { + reject(err); + } + } + resolve(data); + }); + } + /** + * Encodes worker results as an ArrayBuffer so it can be transferred back to + * the main thread. + * + * @param {any[]} results - Array of processed data + */ + static report(results) { + const buffer2 = new TextEncoder().encode(JSON.stringify(results)).buffer; + postMessage(buffer2, [buffer2]); + } + /** + * Listens for messages from the main thread. + * + * Extending classes must call this in a static initialization block: + * ``` + * static { + * Pow.listen() + * } + * ``` + */ + static listen() { + addEventListener("message", (message) => { + const { name, buffer: buffer2 } = message.data; + if (name === "STOP") { + close(); + const buffer3 = new ArrayBuffer(0); + postMessage(buffer3, [buffer3]); + } else { + const data = JSON.parse(new TextDecoder().decode(buffer2)); + this.work(data).then(this.report); + } + }); + } + }; + } +}); + +// src/lib/workers/nano-nacl.ts +var NanoNaCl, nano_nacl_default; +var init_nano_nacl = __esm({ + "src/lib/workers/nano-nacl.ts"() { + "use strict"; + init_blake2b2(); + init_pool(); + NanoNaCl = class _NanoNaCl extends WorkerInterface { + static { + _NanoNaCl.listen(); + } + static async work(data) { + return new Promise(async (resolve, reject) => { + for (let d of data) { + try { + d.publicKey = await this.convert(d.privateKey); + } catch (err) { + reject(err); + } + } + resolve(data); + }); + } + static gf = function(init) { + const r = new Float64Array(16); + if (init) for (let i = 0; i < init.length; i++) r[i] = init[i]; + return r; + }; + static gf0 = this.gf(); + static gf1 = this.gf([1]); + static D = this.gf([30883, 4953, 19914, 30187, 55467, 16705, 2637, 112, 59544, 30585, 16505, 36039, 65139, 11119, 27886, 20995]); + static D2 = this.gf([61785, 9906, 39828, 60374, 45398, 33411, 5274, 224, 53552, 61171, 33010, 6542, 64743, 22239, 55772, 9222]); + static X = this.gf([54554, 36645, 11616, 51542, 42930, 38181, 51040, 26924, 56412, 64982, 57905, 49316, 21502, 52590, 14035, 8553]); + static Y = this.gf([26200, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214]); + static I = this.gf([41136, 18958, 6951, 50414, 58488, 44335, 6150, 12099, 55207, 15867, 153, 11085, 57099, 20417, 9344, 11139]); + static vn(x, xi, y, yi, n) { + let d = 0; + for (let i = 0; i < n; i++) d |= x[xi + i] ^ y[yi + i]; + return (1 & d - 1 >>> 8) - 1; + } + static crypto_verify_32(x, xi, y, yi) { + return this.vn(x, xi, y, yi, 32); + } + static set25519(r, a) { + for (let i = 0; i < 16; i++) r[i] = a[i] | 0; + } + static car25519(o) { + let v, c = 1; + for (let i = 0; i < 16; i++) { + v = o[i] + c + 65535; + c = Math.floor(v / 65536); + o[i] = v - c * 65536; + } + o[0] += c - 1 + 37 * (c - 1); + } + static sel25519(p, q, b) { + let t; + const c = ~(b - 1); + for (let i = 0; i < 16; i++) { + t = c & (p[i] ^ q[i]); + p[i] ^= t; + q[i] ^= t; + } + } + static pack25519(o, n) { + let b; + const m = this.gf(); + const t = this.gf(); + for (let i = 0; i < 16; i++) t[i] = n[i]; + this.car25519(t); + this.car25519(t); + this.car25519(t); + for (let j = 0; j < 2; j++) { + m[0] = t[0] - 65517; + for (let i = 1; i < 15; i++) { + m[i] = t[i] - 65535 - (m[i - 1] >> 16 & 1); + m[i - 1] &= 65535; + } + m[15] = t[15] - 32767 - (m[14] >> 16 & 1); + b = m[15] >> 16 & 1; + m[14] &= 65535; + this.sel25519(t, m, 1 - b); } for (let i = 0; i < 16; i++) { o[2 * i] = t[i] & 255; @@ -1475,17 +1989,14 @@ var init_nano_nacl = __esm({ return d[0] & 1; } static unpack25519(o, n) { - for (let i = 0; i < 16; i++) - o[i] = n[2 * i] + (n[2 * i + 1] << 8); + for (let i = 0; i < 16; i++) o[i] = n[2 * i] + (n[2 * i + 1] << 8); o[15] &= 32767; } static A(o, a, b) { - for (let i = 0; i < 16; i++) - o[i] = a[i] + b[i]; + for (let i = 0; i < 16; i++) o[i] = a[i] + b[i]; } static Z(o, a, b) { - for (let i = 0; i < 16; i++) - o[i] = a[i] - b[i]; + for (let i = 0; i < 16; i++) o[i] = a[i] - b[i]; } static M(o, a, b) { let v, c, t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0, t8 = 0, t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0, t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0, t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0, b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5], b6 = b[6], b7 = b[7], b8 = b[8], b9 = b[9], b10 = b[10], b11 = b[11], b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15]; @@ -1898,36 +2409,28 @@ var init_nano_nacl = __esm({ } static inv25519(o, i) { const c = this.gf(); - for (let a = 0; a < 16; a++) - c[a] = i[a]; + for (let a = 0; a < 16; a++) c[a] = i[a]; for (let a = 253; a >= 0; a--) { this.S(c, c); - if (a !== 2 && a !== 4) - this.M(c, c, i); + if (a !== 2 && a !== 4) this.M(c, c, i); } - for (let a = 0; a < 16; a++) - o[a] = c[a]; + for (let a = 0; a < 16; a++) o[a] = c[a]; } static pow2523(o, i) { const c = this.gf(); - for (let a = 0; a < 16; a++) - c[a] = i[a]; + for (let a = 0; a < 16; a++) c[a] = i[a]; for (let a = 250; a >= 0; a--) { this.S(c, c); - if (a !== 1) - this.M(c, c, i); + if (a !== 1) this.M(c, c, i); } - for (let a = 0; a < 16; a++) - o[a] = c[a]; + for (let a = 0; a < 16; a++) o[a] = c[a]; } // Note: difference from TweetNaCl - BLAKE2b used to hash instead of SHA-512. static crypto_hash(out, m, n) { const input = new Uint8Array(n); - for (let i = 0; i < n; ++i) - input[i] = m[i]; - const hash2 = new Blake2b(64).update(m).digest(); - for (let i = 0; i < 64; ++i) - out[i] = hash2[i]; + for (let i = 0; i < n; ++i) input[i] = m[i]; + const hash2 = new Blake2b2(64).update(m).digest(); + for (let i = 0; i < 64; ++i) out[i] = hash2[i]; return 0; } static add(p, q) { @@ -2047,19 +2550,16 @@ var init_nano_nacl = __esm({ carry = x[j] >> 8; x[j] &= 255; } - for (j = 0; j < 32; j++) - x[j] -= carry * this.L[j]; + for (j = 0; j < 32; j++) x[j] -= carry * this.L[j]; for (i = 0; i < 32; i++) { x[i + 1] += x[i] >> 8; r[i] = x[i] & 255; } - } - static reduce(r) { - let x = new Float64Array(64); - for (let i = 0; i < 64; i++) - x[i] = r[i]; - for (let i = 0; i < 64; i++) - r[i] = 0; + } + static reduce(r) { + let x = new Float64Array(64); + for (let i = 0; i < 64; i++) x[i] = r[i]; + for (let i = 0; i < 64; i++) r[i] = 0; this.modL(r, x); } // Note: difference from C - smlen returned, not passed as argument. @@ -2074,22 +2574,17 @@ var init_nano_nacl = __esm({ d[31] &= 127; d[31] |= 64; const smlen = n + 64; - for (let i = 0; i < n; i++) - sm[64 + i] = m[i]; - for (let i = 0; i < 32; i++) - sm[32 + i] = d[32 + i]; + for (let i = 0; i < n; i++) sm[64 + i] = m[i]; + for (let i = 0; i < 32; i++) sm[32 + i] = d[32 + i]; this.crypto_hash(r, sm.subarray(32), n + 32); this.reduce(r); this.scalarbase(p, r); this.pack(sm, p); - for (let i = 0; i < 32; i++) - sm[i + 32] = pk[i]; + for (let i = 0; i < 32; i++) sm[i + 32] = pk[i]; this.crypto_hash(h, sm, n + 64); this.reduce(h); - for (let i = 0; i < 64; i++) - x[i] = 0; - for (let i = 0; i < 32; i++) - x[i] = r[i]; + for (let i = 0; i < 64; i++) x[i] = 0; + for (let i = 0; i < 32; i++) x[i] = r[i]; for (let i = 0; i < 32; i++) { for (let j = 0; j < 32; j++) { x[i + j] += h[i] * d[j]; @@ -2124,14 +2619,11 @@ var init_nano_nacl = __esm({ this.M(r[0], t, den); this.S(chk, r[0]); this.M(chk, chk, den); - if (this.neq25519(chk, num)) - this.M(r[0], r[0], this.I); + if (this.neq25519(chk, num)) this.M(r[0], r[0], this.I); this.S(chk, r[0]); this.M(chk, chk, den); - if (this.neq25519(chk, num)) - return -1; - if (this.par25519(r[0]) === p[31] >> 7) - this.Z(r[0], this.gf0, r[0]); + if (this.neq25519(chk, num)) return -1; + if (this.par25519(r[0]) === p[31] >> 7) this.Z(r[0], this.gf0, r[0]); this.M(r[3], r[0], r[1]); return 0; } @@ -2140,14 +2632,10 @@ var init_nano_nacl = __esm({ const h = new Uint8Array(64); const p = [this.gf(), this.gf(), this.gf(), this.gf()]; const q = [this.gf(), this.gf(), this.gf(), this.gf()]; - if (n < 64) - return -1; - if (this.unpackneg(q, pk)) - return -1; - for (let i = 0; i < n; i++) - m[i] = sm[i]; - for (let i = 0; i < 32; i++) - m[i + 32] = pk[i]; + if (n < 64) return -1; + if (this.unpackneg(q, pk)) return -1; + for (let i = 0; i < n; i++) m[i] = sm[i]; + for (let i = 0; i < 32; i++) m[i + 32] = pk[i]; this.crypto_hash(h, m, n); this.reduce(h); this.scalarmult(p, q, h); @@ -2156,12 +2644,10 @@ var init_nano_nacl = __esm({ this.pack(t, p); n -= 64; if (this.crypto_verify_32(sm, 0, t, 0)) { - for (let i = 0; i < n; i++) - m[i] = 0; + for (let i = 0; i < n; i++) m[i] = 0; return -1; } - for (let i = 0; i < n; i++) - m[i] = sm[i + 64]; + for (let i = 0; i < n; i++) m[i] = sm[i + 64]; return n; } static crypto_sign_BYTES = 64; @@ -2176,8 +2662,7 @@ var init_nano_nacl = __esm({ } } static parseHex(hex2) { - if (hex2.length % 2 === 1) - hex2 = `0${hex2}`; + if (hex2.length % 2 === 1) hex2 = `0${hex2}`; const arr = hex2.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16)); return Uint8Array.from(arr ?? []); } @@ -2207,18 +2692,15 @@ var init_nano_nacl = __esm({ throw new Error("bad public key size"); const tmp = new Uint8Array(signedMsg.length); var mlen = this.crypto_sign_open(tmp, signedMsg, signedMsg.length, publicKey); - if (mlen < 0) - return new Uint8Array(0); + if (mlen < 0) return new Uint8Array(0); var m = new Uint8Array(mlen); - for (var i = 0; i < m.length; i++) - m[i] = tmp[i]; + for (var i = 0; i < m.length; i++) m[i] = tmp[i]; return m; } static detached(msg, secretKey) { var signedMsg = this.sign(msg, secretKey); var sig = new Uint8Array(this.crypto_sign_BYTES); - for (var i = 0; i < sig.length; i++) - sig[i] = signedMsg[i]; + for (var i = 0; i < sig.length; i++) sig[i] = signedMsg[i]; return this.hexify(sig).toUpperCase(); } static verify(msg, sig, publicKey) { @@ -2229,21 +2711,18 @@ var init_nano_nacl = __esm({ throw new Error("bad public key size"); const sm = new Uint8Array(this.crypto_sign_BYTES + msg.length); const m = new Uint8Array(this.crypto_sign_BYTES + msg.length); - for (let i = 0; i < this.crypto_sign_BYTES; i++) - sm[i] = sig[i]; - for (let i = 0; i < msg.length; i++) - sm[i + this.crypto_sign_BYTES] = msg[i]; + for (let i = 0; i < this.crypto_sign_BYTES; i++) sm[i] = sig[i]; + for (let i = 0; i < msg.length; i++) sm[i + this.crypto_sign_BYTES] = msg[i]; return this.crypto_sign_open(m, sm, sm.length, publicKey) >= 0; } static convert(seed) { - if (typeof seed === "string") - seed = this.parseHex(seed); + if (typeof seed === "string") seed = this.parseHex(seed); this.checkArrayTypes(seed); if (seed.length !== this.crypto_sign_SEEDBYTES) throw new Error("bad seed size"); const pk = new Uint8Array(this.crypto_sign_PUBLICKEYBYTES); const p = [this.gf(), this.gf(), this.gf(), this.gf()]; - const hash2 = new Blake2b(64).update(seed).digest(); + const hash2 = new Blake2b2(64).update(seed).digest(); hash2[0] &= 248; hash2[31] &= 127; hash2[31] |= 64; @@ -2253,7 +2732,7 @@ var init_nano_nacl = __esm({ } }; nano_nacl_default = ` - const Blake2b = ${Blake2b} + const Blake2b = ${Blake2b2} const WorkerInterface = ${WorkerInterface} const NanoNaCl = ${NanoNaCl} `; @@ -2505,187 +2984,1362 @@ var init_account = __esm({ const encodedChecksum = bytes.toBase32(checksumBytes); return `${PREFIX}${encodedPublicKey}${encodedChecksum}`; } - static #validateKey(key) { - if (key === void 0) { - throw new TypeError(`Key is undefined`); + static #validateKey(key) { + if (key === void 0) { + throw new TypeError(`Key is undefined`); + } + if (typeof key !== "string") { + throw new TypeError(`Key must be a string`); + } + if (key.length !== ACCOUNT_KEY_LENGTH) { + throw new TypeError(`Key must be ${ACCOUNT_KEY_LENGTH} characters`); + } + if (!/^[0-9a-fA-F]+$/i.test(key)) { + throw new RangeError("Key is not a valid hexadecimal value"); + } + } + }; + } +}); + +// dist/lib/pool.js +var Pool2; +var init_pool2 = __esm({ + "dist/lib/pool.js"() { + "use strict"; + Pool2 = class _Pool { + static #cores = Math.max(1, navigator.hardwareConcurrency - 1); + #queue = []; + #threads = []; + #url; + get threadsBusy() { + let n = 0; + for (const thread of this.#threads) { + n += +(thread.job != null); + } + return n; + } + get threadsIdle() { + let n = 0; + for (const thread of this.#threads) { + n += +(thread.job == null); + } + return n; + } + async assign(data) { + if (!(data instanceof ArrayBuffer || Array.isArray(data))) + data = [data]; + return new Promise((resolve, reject) => { + const job = { + id: performance.now(), + results: [], + data, + resolve, + reject + }; + if (this.#queue.length > 0) { + this.#queue.push(job); + } else { + for (const thread of this.#threads) + this.#assign(thread, job); + } + }); + } + /** + * + * @param {string} worker - Stringified worker class + * @param {number} [count=1] - Integer between 1 and CPU thread count shared among all Pools + */ + constructor(worker, count = 1) { + count = Math.min(_Pool.#cores, Math.max(1, Math.floor(Math.abs(count)))); + this.#url = URL.createObjectURL(new Blob([worker], { type: "text/javascript" })); + for (let i = 0; i < count; i++) { + const thread = { + worker: new Worker(this.#url, { type: "module" }), + job: null + }; + thread.worker.addEventListener("message", (message) => { + let result = JSON.parse(new TextDecoder().decode(message.data) || "[]"); + if (!Array.isArray(result)) + result = [result]; + this.#report(thread, result); + }); + this.#threads.push(thread); + _Pool.#cores = Math.max(1, _Pool.#cores - this.#threads.length); + } + } + #assign(thread, job) { + if (job.data instanceof ArrayBuffer) { + if (job.data.byteLength > 0) { + thread.job = job; + thread.worker.postMessage({ buffer: job.data }, [job.data]); + } + } else { + const chunk = 1 + job.data.length / this.threadsIdle; + const next = job.data.slice(0, chunk); + job.data = job.data.slice(chunk); + if (job.data.length === 0) + this.#queue.shift(); + if (next?.length > 0) { + const buffer2 = new TextEncoder().encode(JSON.stringify(next)).buffer; + thread.job = job; + thread.worker.postMessage({ buffer: buffer2 }, [buffer2]); + } + } + } + #isJobDone(jobId) { + for (const thread of this.#threads) { + if (thread.job?.id === jobId) + return false; + } + return true; + } + #report(thread, results) { + if (thread.job == null) { + throw new Error("Thread returned results but had nowhere to report it."); + } + const job = thread.job; + if (this.#queue.length > 0) { + this.#assign(thread, this.#queue[0]); + } else { + thread.job = null; + } + if (results.length > 0) { + job.results.push(...results); + } + if (this.#isJobDone(job.id)) { + job.resolve(job.results); + } + } + }; + } +}); + +// src/lib/workers/bip44-ckd.ts +var Bip44Ckd, bip44_ckd_default; +var init_bip44_ckd = __esm({ + "src/lib/workers/bip44-ckd.ts"() { + "use strict"; + init_pool(); + Bip44Ckd = class _Bip44Ckd extends WorkerInterface { + static BIP44_COIN_NANO = 165; + static BIP44_PURPOSE = 44; + static HARDENED_OFFSET = 2147483648; + static SLIP10_ED25519 = "ed25519 seed"; + static { + _Bip44Ckd.listen(); + } + static async work(data) { + for (const d of data) { + if (d.coin != null && d.coin !== this.BIP44_PURPOSE) { + d.privateKey = await this.ckd(d.seed, d.coin, d.index); + } else { + d.privateKey = await this.nanoCKD(d.seed, d.index); + } + } + return data; + } + /** + * Derives a private child key following the BIP-32 and BIP-44 derivation path + * registered to the Nano block lattice. Only hardened child keys are defined. + * + * @param {string} seed - Hexadecimal seed derived from mnemonic phrase + * @param {number} index - Account number between 0 and 2^31-1 + * @returns {Promise} Private child key for the account + */ + static async nanoCKD(seed, index) { + if (!Number.isSafeInteger(index) || index < 0 || index > 2147483647) { + throw new RangeError(`Invalid child key index 0x${index.toString(16)}`); + } + return await this.ckd(seed, this.BIP44_COIN_NANO, index); + } + /** + * Derives a private child key for a coin by following the specified BIP-32 and + * BIP-44 derivation path. Purpose is always 44'. Only hardened child keys are + * defined. + * + * @param {string} seed - Hexadecimal seed derived from mnemonic phrase + * @param {number} coin - Number registered to a specific coin in SLIP-044 + * @param {number} index - Account number between 0 and 2^31-1 + * @returns {Promise} Private child key for the account + */ + static async ckd(seed, coin, index) { + if (seed.length < 32 || seed.length > 128) { + throw new RangeError(`Invalid seed length`); + } + if (!Number.isSafeInteger(index) || index < 0 || index > 2147483647) { + throw new RangeError(`Invalid child key index 0x${index.toString(16)}`); + } + const masterKey = await this.slip10(this.SLIP10_ED25519, seed); + const purposeKey = await this.CKDpriv(masterKey, this.BIP44_PURPOSE + this.HARDENED_OFFSET); + const coinKey = await this.CKDpriv(purposeKey, coin + this.HARDENED_OFFSET); + const accountKey = await this.CKDpriv(coinKey, index + this.HARDENED_OFFSET); + const privateKey = new Uint8Array(accountKey.privateKey.buffer); + let hex2 = ""; + for (let i = 0; i < privateKey.length; i++) { + hex2 += privateKey[i].toString(16).padStart(2, "0"); + } + return hex2; + } + static async slip10(curve, S) { + const key = new TextEncoder().encode(curve); + const data = new Uint8Array(64); + data.set(S.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))); + const I = await this.hmac(key, data); + const IL = new DataView(I.buffer.slice(0, I.length / 2)); + const IR = new DataView(I.buffer.slice(I.length / 2)); + return { privateKey: IL, chainCode: IR }; + } + static async CKDpriv({ privateKey, chainCode }, index) { + const key = new Uint8Array(chainCode.buffer); + const data = new Uint8Array(37); + data.set([0]); + data.set(this.ser256(privateKey), 1); + data.set(this.ser32(index), 33); + const I = await this.hmac(key, data); + const IL = new DataView(I.buffer.slice(0, I.length / 2)); + const IR = new DataView(I.buffer.slice(I.length / 2)); + return { privateKey: IL, chainCode: IR }; + } + static ser32(integer) { + if (typeof integer !== "number") { + throw new TypeError(`Expected a number, received ${typeof integer}`); } - if (typeof key !== "string") { - throw new TypeError(`Key must be a string`); + if (integer > 4294967295) { + throw new RangeError(`Expected 32-bit integer, received ${integer.toString(2).length}-bit value: ${integer}`); } - if (key.length !== ACCOUNT_KEY_LENGTH) { - throw new TypeError(`Key must be ${ACCOUNT_KEY_LENGTH} characters`); + const view = new DataView(new ArrayBuffer(4)); + view.setUint32(0, integer, false); + return new Uint8Array(view.buffer); + } + static ser256(integer) { + if (integer.constructor !== DataView) { + throw new TypeError(`Expected DataView, received ${typeof integer}`); } - if (!/^[0-9a-fA-F]+$/i.test(key)) { - throw new RangeError("Key is not a valid hexadecimal value"); + if (integer.byteLength > 32) { + throw new RangeError(`Expected 32-byte integer, received ${integer.byteLength}-byte value: ${integer}`); } + return new Uint8Array(integer.buffer); + } + static async hmac(key, data) { + const { subtle: subtle3 } = globalThis.crypto; + const pk = await subtle3.importKey("raw", key, { name: "HMAC", hash: "SHA-512" }, false, ["sign"]); + const signature = await subtle3.sign("HMAC", pk, data); + return new Uint8Array(signature); } }; + bip44_ckd_default = ` + const WorkerInterface = ${WorkerInterface} + const Bip44Ckd = ${Bip44Ckd} +`; } }); -// dist/lib/workers/bip44-ckd.js -var Bip44Ckd, bip44_ckd_default; -var init_bip44_ckd = __esm({ - "dist/lib/workers/bip44-ckd.js"() { - "use strict"; - init_pool(); - Bip44Ckd = class _Bip44Ckd extends WorkerInterface { - static BIP44_COIN_NANO = 165; - static BIP44_PURPOSE = 44; - static HARDENED_OFFSET = 2147483648; - static SLIP10_ED25519 = "ed25519 seed"; - static { - _Bip44Ckd.listen(); - } - static async work(data) { - for (const d of data) { - if (d.coin != null && d.coin !== this.BIP44_PURPOSE) { - d.privateKey = await this.ckd(d.seed, d.coin, d.index); - } else { - d.privateKey = await this.nanoCKD(d.seed, d.index); - } - } - return data; - } - /** - * Derives a private child key following the BIP-32 and BIP-44 derivation path - * registered to the Nano block lattice. Only hardened child keys are defined. - * - * @param {string} seed - Hexadecimal seed derived from mnemonic phrase - * @param {number} index - Account number between 0 and 2^31-1 - * @returns {Promise} Private child key for the account - */ - static async nanoCKD(seed, index) { - if (!Number.isSafeInteger(index) || index < 0 || index > 2147483647) { - throw new RangeError(`Invalid child key index 0x${index.toString(16)}`); - } - return await this.ckd(seed, this.BIP44_COIN_NANO, index); - } - /** - * Derives a private child key for a coin by following the specified BIP-32 and - * BIP-44 derivation path. Purpose is always 44'. Only hardened child keys are - * defined. - * - * @param {string} seed - Hexadecimal seed derived from mnemonic phrase - * @param {number} coin - Number registered to a specific coin in SLIP-044 - * @param {number} index - Account number between 0 and 2^31-1 - * @returns {Promise} Private child key for the account - */ - static async ckd(seed, coin, index) { - if (seed.length < 32 || seed.length > 128) { - throw new RangeError(`Invalid seed length`); - } - if (!Number.isSafeInteger(index) || index < 0 || index > 2147483647) { - throw new RangeError(`Invalid child key index 0x${index.toString(16)}`); - } - const masterKey = await this.slip10(this.SLIP10_ED25519, seed); - const purposeKey = await this.CKDpriv(masterKey, this.BIP44_PURPOSE + this.HARDENED_OFFSET); - const coinKey = await this.CKDpriv(purposeKey, coin + this.HARDENED_OFFSET); - const accountKey = await this.CKDpriv(coinKey, index + this.HARDENED_OFFSET); - const privateKey = new Uint8Array(accountKey.privateKey.buffer); - let hex2 = ""; - for (let i = 0; i < privateKey.length; i++) { - hex2 += privateKey[i].toString(16).padStart(2, "0"); - } - return hex2; - } - static async slip10(curve, S) { - const key = new TextEncoder().encode(curve); - const data = new Uint8Array(64); - data.set(S.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))); - const I = await this.hmac(key, data); - const IL = new DataView(I.buffer.slice(0, I.length / 2)); - const IR = new DataView(I.buffer.slice(I.length / 2)); - return { privateKey: IL, chainCode: IR }; - } - static async CKDpriv({ privateKey, chainCode }, index) { - const key = new Uint8Array(chainCode.buffer); - const data = new Uint8Array(37); - data.set([0]); - data.set(this.ser256(privateKey), 1); - data.set(this.ser32(index), 33); - const I = await this.hmac(key, data); - const IL = new DataView(I.buffer.slice(0, I.length / 2)); - const IR = new DataView(I.buffer.slice(I.length / 2)); - return { privateKey: IL, chainCode: IR }; - } - static ser32(integer) { - if (typeof integer !== "number") { - throw new TypeError(`Expected a number, received ${typeof integer}`); - } - if (integer > 4294967295) { - throw new RangeError(`Expected 32-bit integer, received ${integer.toString(2).length}-bit value: ${integer}`); - } - const view = new DataView(new ArrayBuffer(4)); - view.setUint32(0, integer, false); - return new Uint8Array(view.buffer); - } - static ser256(integer) { - if (integer.constructor !== DataView) { - throw new TypeError(`Expected DataView, received ${typeof integer}`); - } - if (integer.byteLength > 32) { - throw new RangeError(`Expected 32-byte integer, received ${integer.byteLength}-byte value: ${integer}`); - } - return new Uint8Array(integer.buffer); - } - static async hmac(key, data) { - const { subtle: subtle3 } = globalThis.crypto; - const pk = await subtle3.importKey("raw", key, { name: "HMAC", hash: "SHA-512" }, false, ["sign"]); - const signature = await subtle3.sign("HMAC", pk, data); - return new Uint8Array(signature); - } - }; - bip44_ckd_default = ` - const WorkerInterface = ${WorkerInterface} - const Bip44Ckd = ${Bip44Ckd} +// src/lib/nano-pow/shaders/gpu-compute.ts +var NanoPowGpuComputeShader; +var init_gpu_compute = __esm({ + "src/lib/nano-pow/shaders/gpu-compute.ts"() { + "use strict"; + { + } + NanoPowGpuComputeShader = ` +struct UBO { + blockhash: array, 2>, + random: u32, + threshold: u32 +}; +@group(0) @binding(0) var ubo: UBO; + +struct WORK { + nonce: vec2, + found: atomic +}; +@group(0) @binding(1) var work: WORK; + +/** +* Defined separately from uint v[32] below as the original value is required +* to calculate the second uint32 of the digest for threshold comparison +*/ +const BLAKE2B_IV32_1: u32 = 0x6A09E667u; + + +/** +* G Mixing function +*/ +fn G ( + va0: ptr, va1: ptr, + vb0: ptr, vb1: ptr, + vc0: ptr, vc1: ptr, + vd0: ptr, vd1: ptr, + mx0: u32, mx1: u32, my0: u32, my1: u32 +) { + var o0: u32; + var o1: u32; + var xor0: u32; + var xor1: u32; + + // a = a + b; + o0 = *va0 + *vb0; + o1 = *va1 + *vb1; + if (*va0 > 0xFFFFFFFFu - *vb0) { + o1 = o1 + 1u; + } + *va0 = o0; + *va1 = o1; + + // a = a + m[sigma[r][2*i+0]]; + o0 = *va0 + mx0; + o1 = *va1 + mx1; + if (*va0 > 0xFFFFFFFFu - mx0) { + o1 = o1 + 1u; + } + *va0 = o0; + *va1 = o1; + + // d = rotr64(d ^ a, 32); + xor0 = *vd0 ^ *va0; + xor1 = *vd1 ^ *va1; + *vd0 = xor1; + *vd1 = xor0; + + // c = c + d; + o0 = *vc0 + *vd0; + o1 = *vc1 + *vd1; + if (*vc0 > 0xFFFFFFFFu - *vd0) { + o1 = o1 + 1u; + } + *vc0 = o0; + *vc1 = o1; + + // b = rotr64(b ^ c, 24); + xor0 = *vb0 ^ *vc0; + xor1 = *vb1 ^ *vc1; + *vb0 = (xor0 >> 24u) ^ (xor1 << 8u); + *vb1 = (xor1 >> 24u) ^ (xor0 << 8u); + + // a = a + b; + o0 = *va0 + *vb0; + o1 = *va1 + *vb1; + if (*va0 > 0xFFFFFFFFu - *vb0) { + o1 = o1 + 1u; + } + *va0 = o0; + *va1 = o1; + + // a = a + m[sigma[r][2*i+1]]; + o0 = *va0 + my0; + o1 = *va1 + my1; + if (*va0 > 0xFFFFFFFFu - my0) { + o1 = o1 + 1u; + } + *va0 = o0; + *va1 = o1; + + // d = rotr64(d ^ a, 16) + xor0 = *vd0 ^ *va0; + xor1 = *vd1 ^ *va1; + *vd0 = (xor0 >> 16u) ^ (xor1 << 16u); + *vd1 = (xor1 >> 16u) ^ (xor0 << 16u); + + // c = c + d; + o0 = *vc0 + *vd0; + o1 = *vc1 + *vd1; + if (*vc0 > 0xFFFFFFFFu - *vd0) { + o1 = o1 + 1u; + } + *vc0 = o0; + *vc1 = o1; + + // b = rotr64(b ^ c, 63) + xor0 = *vb0 ^ *vc0; + xor1 = *vb1 ^ *vc1; + *vb0 = (xor1 >> 31u) ^ (xor0 << 1u); + *vb1 = (xor0 >> 31u) ^ (xor1 << 1u); +} + +/** +* Main compute function +* 8-byte work is split into two 4-byte u32. Low 4 bytes are random u32 from +* UBO. High 4 bytes are the random value XOR'd with index of each thread. +*/ +@compute @workgroup_size(64) +fn main( + @builtin(workgroup_id) workgroup_id: vec3, + @builtin(local_invocation_id) local_id: vec3 +) { + if (atomicLoad(&work.found) != 0u) { return; } + + let threshold: u32 = ubo.threshold; + + /** + * Flatten 3D workgroup and local identifiers into u32 for each thread + */ + var id: u32 = ((workgroup_id.x & 0xFFu) << 24u) | + ((workgroup_id.y & 0xFFu) << 16u) | + ((workgroup_id.z & 0xFFu) << 8u) | + (local_id.x & 0xFFu); + + /** + * Initialize (nonce||blockhash) concatenation + */ + var m0: u32 = ubo.random; + var m1: u32 = ubo.random ^ id; + var m2: u32 = ubo.blockhash[0u].x; + var m3: u32 = ubo.blockhash[0u].y; + var m4: u32 = ubo.blockhash[0u].z; + var m5: u32 = ubo.blockhash[0u].w; + var m6: u32 = ubo.blockhash[1u].x; + var m7: u32 = ubo.blockhash[1u].y; + var m8: u32 = ubo.blockhash[1u].z; + var m9: u32 = ubo.blockhash[1u].w; + + /** + * Compression buffer intialized to 2 instances of initialization vector + * The following values have been modified from the BLAKE2B_IV: + * OUTLEN is constant 8 bytes + * v[0u] ^= 0x01010000u ^ uint(OUTLEN); + * INLEN is constant 40 bytes: work value (8) + block hash (32) + * v[24u] ^= uint(INLEN); + * It is always the "last" compression at this INLEN + * v[28u] = ~v[28u]; + * v[29u] = ~v[29u]; + */ + var v0: u32 = 0xF2BDC900u; + var v1: u32 = 0x6A09E667u; + var v2: u32 = 0x84CAA73Bu; + var v3: u32 = 0xBB67AE85u; + var v4: u32 = 0xFE94F82Bu; + var v5: u32 = 0x3C6EF372u; + var v6: u32 = 0x5F1D36F1u; + var v7: u32 = 0xA54FF53Au; + var v8: u32 = 0xADE682D1u; + var v9: u32 = 0x510E527Fu; + var v10: u32 = 0x2B3E6C1Fu; + var v11: u32 = 0x9B05688Cu; + var v12: u32 = 0xFB41BD6Bu; + var v13: u32 = 0x1F83D9ABu; + var v14: u32 = 0x137E2179u; + var v15: u32 = 0x5BE0CD19u; + var v16: u32 = 0xF3BCC908u; + var v17: u32 = 0x6A09E667u; + var v18: u32 = 0x84CAA73Bu; + var v19: u32 = 0xBB67AE85u; + var v20: u32 = 0xFE94F82Bu; + var v21: u32 = 0x3C6EF372u; + var v22: u32 = 0x5F1D36F1u; + var v23: u32 = 0xA54FF53Au; + var v24: u32 = 0xADE682F9u; + var v25: u32 = 0x510E527Fu; + var v26: u32 = 0x2B3E6C1Fu; + var v27: u32 = 0x9B05688Cu; + var v28: u32 = 0x04BE4294u; + var v29: u32 = 0xE07C2654u; + var v30: u32 = 0x137E2179u; + var v31: u32 = 0x5BE0CD19u; + + /** + * Twelve rounds of G mixing as part of BLAKE2b compression step. + * Each sigma r index correlates with the reference implementation, but each + * sigma i index, and each v index, is doubled due to using two u32 array + * elements to represent one uint64_t. + */ + var o0: u32; + var o1: u32; + var xor0: u32; + var xor1: u32; + + /**************************************************************************** + * ROUND(0) * + ****************************************************************************/ + + /** + * r=0, i=0(x2), a=v[0-1], b=v[8-9], c=v[16-17], d=v[24-25] + */ + + // a = a + b; + o0 = v0 + v8; + o1 = v1 + v9; + if (v0 > 0xFFFFFFFFu - v8) { + o1 = o1 + 1u; + } + v0 = o0; + v1 = o1; + + // a = a + m[sigma[r][2*i+0]]; + o0 = v0 + m0; + o1 = v1 + m1; + if (v0 > 0xFFFFFFFFu - m0) { + o1 = o1 + 1u; + } + v0 = o0; + v1 = o1; + + // d = rotr64(d ^ a, 32); + xor0 = v24 ^ v0; + xor1 = v25 ^ v1; + v24 = xor1; + v25 = xor0; + + // c = c + d; + o0 = v16 + v24; + o1 = v17 + v25; + if (v16 > 0xFFFFFFFFu - v24) { + o1 = o1 + 1u; + } + v16 = o0; + v17 = o1; + + // b = rotr64(b ^ c, 24); + xor0 = v8 ^ v16; + xor1 = v9 ^ v17; + v8 = (xor0 >> 24u) ^ (xor1 << 8u); + v9 = (xor1 >> 24u) ^ (xor0 << 8u); + + // a = a + b; + o0 = v0 + v8; + o1 = v1 + v9; + if (v0 > 0xFFFFFFFFu - v8) { + o1 = o1 + 1u; + } + v0 = o0; + v1 = o1; + + // a = a + m[sigma[r][2*i+1]]; + o0 = v0 + m2; + o1 = v1 + m3; + if (v0 > 0xFFFFFFFFu - m2) { + o1 = o1 + 1u; + } + v0 = o0; + v1 = o1; + + // d = rotr64(d ^ a, 16) + xor0 = v24 ^ v0; + xor1 = v25 ^ v1; + v24 = (xor0 >> 16u) ^ (xor1 << 16u); + v25 = (xor1 >> 16u) ^ (xor0 << 16u); + + // c = c + d; + o0 = v16 + v24; + o1 = v17 + v25; + if (v16 > 0xFFFFFFFFu - v24) { + o1 = o1 + 1u; + } + v16 = o0; + v17 = o1; + + // b = rotr64(b ^ c, 63) + xor0 = v8 ^ v16; + xor1 = v9 ^ v17; + v8 = (xor1 >> 31u) ^ (xor0 << 1u); + v9 = (xor0 >> 31u) ^ (xor1 << 1u); + + + + + + /** + * r=0, i=1(x2), a=v[2-3], b=v[10-11], c=v[18-19], d=v[26-27] + */ + + // a = a + b; + o0 = v2 + v10; + o1 = v3 + v11; + if (v2 > 0xFFFFFFFFu - v10) { + o1 = o1 + 1u; + } + v2 = o0; + v3 = o1; + + // a = a + m[sigma[r][2*i+0]]; + o0 = v2 + m4; + o1 = v3 + m5; + if (v2 > 0xFFFFFFFFu - m4) { + o1 = o1 + 1u; + } + v2 = o0; + v3 = o1; + + // d = rotr64(d ^ a, 32); + xor0 = v26 ^ v2; + xor1 = v27 ^ v3; + v26 = xor1; + v27 = xor0; + + // c = c + d; + o0 = v18 + v26; + o1 = v19 + v27; + if (v18 > 0xFFFFFFFFu - v26) { + o1 = o1 + 1u; + } + v18 = o0; + v19 = o1; + + // b = rotr64(b ^ c, 24); + xor0 = v10 ^ v18; + xor1 = v11 ^ v19; + v10 = (xor0 >> 24u) ^ (xor1 << 8u); + v11 = (xor1 >> 24u) ^ (xor0 << 8u); + + // a = a + b; + o0 = v2 + v10; + o1 = v3 + v11; + if (v2 > 0xFFFFFFFFu - v10) { + o1 = o1 + 1u; + } + v2 = o0; + v3 = o1; + + // a = a + m[sigma[r][2*i+1]]; + o0 = v2 + m6; + o1 = v3 + m7; + if (v2 > 0xFFFFFFFFu - m6) { + o1 = o1 + 1u; + } + v2 = o0; + v3 = o1; + + // d = rotr64(d ^ a, 16) + xor0 = v26 ^ v2; + xor1 = v27 ^ v3; + v26 = (xor0 >> 16u) ^ (xor1 << 16u); + v27 = (xor1 >> 16u) ^ (xor0 << 16u); + + // c = c + d; + o0 = v18 + v26; + o1 = v19 + v27; + if (v18 > 0xFFFFFFFFu - v26) { + o1 = o1 + 1u; + } + v18 = o0; + v19 = o1; + + // b = rotr64(b ^ c, 63) + xor0 = v10 ^ v18; + xor1 = v11 ^ v19; + v10 = (xor1 >> 31u) ^ (xor0 << 1u); + v11 = (xor0 >> 31u) ^ (xor1 << 1u); + + + + + + /** + * r=0, i=2(x2), a=v[2-3], b=v[10-11], c=v[18-19], d=v[26-27] + */ + + // a = a + b + o0 = v4 + v12; + o1 = v5 + v13; + if (v4 > 0xFFFFFFFFu - v12) { + o1 = o1 + 1u; + } + v4 = o0; + v5 = o1; + + // a = a + m[sigma[r][2*i+0]] + o0 = v4 + m8; + o1 = v5 + m9; + if (v4 > 0xFFFFFFFFu - m8) { + o1 = o1 + 1u; + } + v4 = o0; + v5 = o1; + + // d = rotr64(d ^ a, 32) + xor0 = v28 ^ v4; + xor1 = v29 ^ v5; + v28 = xor1; + v29 = xor0; + + // c = c + d + o0 = v20 + v28; + o1 = v21 + v29; + if (v20 > 0xFFFFFFFFu - v28) { + o1 = o1 + 1u; + } + v20 = o0; + v21 = o1; + + // b = rotr64(b ^ c, 24) + xor0 = v12 ^ v20; + xor1 = v13 ^ v21; + v12 = (xor0 >> 24u) ^ (xor1 << 8u); + v13 = (xor1 >> 24u) ^ (xor0 << 8u); + + // a = a + b + o0 = v4 + v12; + o1 = v5 + v13; + if (v4 > 0xFFFFFFFFu - v12) { + o1 = o1 + 1u; + } + v4 = o0; + v5 = o1; + + // // a = a + m[sigma[r][2*i+1]] + // // skip since adding 0u does nothing + // o0 = v4 + 0u; + // o1 = v5 + 0u; + // if (v4 > 0xFFFFFFFFu - 0u) { + // o1 = o1 + 1u; + // } + // v4 = o0; + // v5 = o1; + + // d = rotr64(d ^ a, 16) + xor0 = v28 ^ v4; + xor1 = v29 ^ v5; + v28 = (xor0 >> 16u) ^ (xor1 << 16u); + v29 = (xor1 >> 16u) ^ (xor0 << 16u); + + // c = c + d + o0 = v20 + v28; + o1 = v21 + v29; + if (v20 > 0xFFFFFFFFu - v28) { + o1 = o1 + 1u; + } + v20 = o0; + v21 = o1; + + // b = rotr64(b ^ c, 63) + xor0 = v12 ^ v20; + xor1 = v13 ^ v21; + v12 = (xor1 >> 31u) ^ (xor0 << 1u); + v13 = (xor0 >> 31u) ^ (xor1 << 1u); + + + + + + /** + * r=0, i=3(x2), a=v[6-7], b=v[14-15], c=v[22-23], d=v[30-31] + */ + + // a = a + b + o0 = v6 + v14; + o1 = v7 + v15; + if (v6 > 0xFFFFFFFFu - v14) { + o1 = o1 + 1u; + } + v6 = o0; + v7 = o1; + + // // a = a + m[sigma[r][2*i+0]] + // // skip since adding 0u does nothing + // o0 = v6 + 0u; + // o1 = v7 + 0u; + // if (v6 > 0xFFFFFFFFu - 0u) { + // o1 = o1 + 1u; + // } + // v6 = o0; + // v7 = o1; + + // d = rotr64(d ^ a, 32) + xor0 = v30 ^ v6; + xor1 = v31 ^ v7; + v30 = xor1; + v31 = xor0; + + // c = c + d + o0 = v22 + v30; + o1 = v23 + v31; + if (v22 > 0xFFFFFFFFu - v30) { + o1 = o1 + 1u; + } + v22 = o0; + v23 = o1; + + // b = rotr64(b ^ c, 24) + xor0 = v14 ^ v22; + xor1 = v15 ^ v23; + v14 = (xor0 >> 24u) ^ (xor1 << 8u); + v15 = (xor1 >> 24u) ^ (xor0 << 8u); + + // a = a + b + o0 = v6 + v14; + o1 = v7 + v15; + if (v6 > 0xFFFFFFFFu - v14) { + o1 = o1 + 1u; + } + v6 = o0; + v7 = o1; + + // // a = a + m[sigma[r][2*i+1]] + // // skip since adding 0u does nothing + // o0 = v6 + 0u; + // o1 = v7 + 0u; + // if (v6 > 0xFFFFFFFFu - 0u) { + // o1 = o1 + 1u; + // } + // v6 = o0; + // v7 = o1; + + // d = rotr64(d ^ a, 16) + xor0 = v30 ^ v6; + xor1 = v31 ^ v7; + v30 = (xor0 >> 16u) ^ (xor1 << 16u); + v31 = (xor1 >> 16u) ^ (xor0 << 16u); + + // c = c + d + o0 = v22 + v30; + o1 = v23 + v31; + if (v22 > 0xFFFFFFFFu - v30) { + o1 = o1 + 1u; + } + v22 = o0; + v23 = o1; + + // b = rotr64(b ^ c, 63) + xor0 = v14 ^ v22; + xor1 = v15 ^ v23; + v14 = (xor1 >> 31u) ^ (xor0 << 1u); + v15 = (xor0 >> 31u) ^ (xor1 << 1u); + + + + + + /** + * r=0, i=4(x2), a=v[0-1], b=v[10-11], c=v[20-21], d=v[30-31] + */ + + // a = a + b + o0 = v0 + v10; + o1 = v1 + v11; + if (v0 > 0xFFFFFFFFu - v10) { + o1 = o1 + 1u; + } + v0 = o0; + v1 = o1; + + // // a = a + m[sigma[r][2*i+0]] + // // skip since adding 0u does nothing + // o0 = v0 + 0u; + // o1 = v1 + 0u; + // if (v0 > 0xFFFFFFFFu - 0u) { + // o1 = o1 + 1u; + // } + // v0 = o0; + // v1 = o1; + + // d = rotr64(d ^ a, 32) + xor0 = v30 ^ v0; + xor1 = v31 ^ v1; + v30 = xor1; + v31 = xor0; + + // c = c + d + o0 = v20 + v30; + o1 = v21 + v31; + if (v20 > 0xFFFFFFFFu - v30) { + o1 = o1 + 1u; + } + v20 = o0; + v21 = o1; + + // b = rotr64(b ^ c, 24) + xor0 = v10 ^ v20; + xor1 = v11 ^ v21; + v10 = (xor0 >> 24u) ^ (xor1 << 8u); + v11 = (xor1 >> 24u) ^ (xor0 << 8u); + + // a = a + b + o0 = v0 + v10; + o1 = v1 + v11; + if (v0 > 0xFFFFFFFFu - v10) { + o1 = o1 + 1u; + } + v0 = o0; + v1 = o1; + + // // a = a + m[sigma[r][2*i+1]] + // // skip since adding 0u does nothing + // o0 = v0 + 0u; + // o1 = v1 + 0u; + // if (v0 > 0xFFFFFFFFu - 0u) { + // o1 = o1 + 1u; + // } + // v0 = o0; + // v1 = o1; + + // d = rotr64(d ^ a, 16) + xor0 = v30 ^ v0; + xor1 = v31 ^ v1; + v30 = (xor0 >> 16u) ^ (xor1 << 16u); + v31 = (xor1 >> 16u) ^ (xor0 << 16u); + + // c = c + d + o0 = v20 + v30; + o1 = v21 + v31; + if (v20 > 0xFFFFFFFFu - v30) { + o1 = o1 + 1u; + } + v20 = o0; + v21 = o1; + + // b = rotr64(b ^ c, 63) + xor0 = v10 ^ v20; + xor1 = v11 ^ v21; + v10 = (xor1 >> 31u) ^ (xor0 << 1u); + v11 = (xor0 >> 31u) ^ (xor1 << 1u); + + + + + + /** + * r=0, i=5(x2), a=v[2-3], b=v[12-13], c=v[22-23], d=v[24-25] + */ + + // a = a + b + o0 = v2 + v12; + o1 = v3 + v13; + if (v2 > 0xFFFFFFFFu - v12) { + o1 = o1 + 1u; + } + v2 = o0; + v3 = o1; + + // // a = a + m[sigma[r][2*i+0]] + // // skip since adding 0u does nothing + // o0 = v2 + 0u; + // o1 = v3 + 0u; + // if (v2 > 0xFFFFFFFFu - 0u) { + // o1 = o1 + 1u; + // } + // v2 = o0; + // v3 = o1; + + // d = rotr64(d ^ a, 32) + xor0 = v24 ^ v2; + xor1 = v25 ^ v3; + v24 = xor1; + v25 = xor0; + + // c = c + d + o0 = v22 + v24; + o1 = v23 + v25; + if (v22 > 0xFFFFFFFFu - v24) { + o1 = o1 + 1u; + } + v22 = o0; + v23 = o1; + + // b = rotr64(b ^ c, 24) + xor0 = v12 ^ v22; + xor1 = v13 ^ v23; + v12 = (xor0 >> 24u) ^ (xor1 << 8u); + v13 = (xor1 >> 24u) ^ (xor0 << 8u); + + // a = a + b + o0 = v2 + v12; + o1 = v3 + v13; + if (v2 > 0xFFFFFFFFu - v12) { + o1 = o1 + 1u; + } + v2 = o0; + v3 = o1; + + // // a = a + m[sigma[r][2*i+1]] + // // skip since adding 0u does nothing + // o0 = v2 + 0u; + // o1 = v3 + 0u; + // if (v2 > 0xFFFFFFFFu - 0u) { + // o1 = o1 + 1u; + // } + // v2 = o0; + // v3 = o1; + + // d = rotr64(d ^ a, 16) + xor0 = v24 ^ v2; + xor1 = v25 ^ v3; + v24 = (xor0 >> 16u) ^ (xor1 << 16u); + v25 = (xor1 >> 16u) ^ (xor0 << 16u); + + // c = c + d + o0 = v22 + v24; + o1 = v23 + v25; + if (v22 > 0xFFFFFFFFu - v24) { + o1 = o1 + 1u; + } + v22 = o0; + v23 = o1; + + // b = rotr64(b ^ c, 63) + xor0 = v12 ^ v22; + xor1 = v13 ^ v23; + v12 = (xor1 >> 31u) ^ (xor0 << 1u); + v13 = (xor0 >> 31u) ^ (xor1 << 1u); + + + + + + /** + * r=0, i=6(x2), a=v[4-6], b=v[14-15], c=v[16-17], d=v[26-27] + */ + + // a = a + b + o0 = v4 + v14; + o1 = v5 + v15; + if (v4 > 0xFFFFFFFFu - v14) { + o1 = o1 + 1u; + } + v4 = o0; + v5 = o1; + + // // a = a + m[sigma[r][2*i+0]] + // // skip since adding 0u does nothing + // o0 = v4 + 0u; + // o1 = v5 + 0u; + // if (v4 > 0xFFFFFFFFu - 0u) { + // o1 = o1 + 1u; + // } + // v4 = o0; + // v5 = o1; + + // d = rotr64(d ^ a, 32) + xor0 = v26 ^ v4; + xor1 = v27 ^ v5; + v26 = xor1; + v27 = xor0; + + // c = c + d + o0 = v16 + v26; + o1 = v17 + v27; + if (v16 > 0xFFFFFFFFu - v26) { + o1 = o1 + 1u; + } + v16 = o0; + v17 = o1; + + // b = rotr64(b ^ c, 24) + xor0 = v14 ^ v16; + xor1 = v15 ^ v17; + v14 = (xor0 >> 24u) ^ (xor1 << 8u); + v15 = (xor1 >> 24u) ^ (xor0 << 8u); + + // a = a + b + o0 = v4 + v14; + o1 = v5 + v15; + if (v4 > 0xFFFFFFFFu - v14) { + o1 = o1 + 1u; + } + v4 = o0; + v5 = o1; + + // // a = a + m[sigma[r][2*i+1]] + // // skip since adding 0u does nothing + // o0 = v4 + 0u; + // o1 = v5 + 0u; + // if (v4 > 0xFFFFFFFFu - 0u) { + // o1 = o1 + 1u; + // } + // v4 = o0; + // v5 = o1; + + // d = rotr64(d ^ a, 16) + xor0 = v26 ^ v4; + xor1 = v27 ^ v5; + v26 = (xor0 >> 16u) ^ (xor1 << 16u); + v27 = (xor1 >> 16u) ^ (xor0 << 16u); + + // c = c + d + o0 = v16 + v26; + o1 = v17 + v27; + if (v16 > 0xFFFFFFFFu - v26) { + o1 = o1 + 1u; + } + v16 = o0; + v17 = o1; + + // b = rotr64(b ^ c, 63) + xor0 = v14 ^ v16; + xor1 = v15 ^ v17; + v14 = (xor1 >> 31u) ^ (xor0 << 1u); + v15 = (xor0 >> 31u) ^ (xor1 << 1u); + + + + + + /** + * r=0, i=7(x2), a=v[6-7], b=v[8-9], c=v[18-19], d=v[28-29] + */ + + // a = a + b + o0 = v6 + v8; + o1 = v7 + v9; + if (v6 > 0xFFFFFFFFu - v8) { + o1 = o1 + 1u; + } + v6 = o0; + v7 = o1; + + // // a = a + m[sigma[r][2*i+0]] + // // skip since adding 0u does nothing + // o0 = v6 + 0u; + // o1 = v7 + 0u; + // if (v6 > 0xFFFFFFFFu - 0u) { + // o1 = o1 + 1u; + // } + // v6 = o0; + // v7 = o1; + + // d = rotr64(d ^ a, 32) + xor0 = v28 ^ v6; + xor1 = v29 ^ v7; + v28 = xor1; + v29 = xor0; + + // c = c + d + o0 = v18 + v28; + o1 = v19 + v29; + if (v18 > 0xFFFFFFFFu - v28) { + o1 = o1 + 1u; + } + v18 = o0; + v19 = o1; + + // b = rotr64(b ^ c, 24) + xor0 = v8 ^ v18; + xor1 = v9 ^ v19; + v8 = (xor0 >> 24u) ^ (xor1 << 8u); + v9 = (xor1 >> 24u) ^ (xor0 << 8u); + + // a = a + b + o0 = v6 + v8; + o1 = v7 + v9; + if (v6 > 0xFFFFFFFFu - v8) { + o1 = o1 + 1u; + } + v6 = o0; + v7 = o1; + + // // a = a + m[sigma[r][2*i+1]] + // // skip since adding 0u does nothing + // o0 = v6 + 0u; + // o1 = v7 + 0u; + // if (v6 > 0xFFFFFFFFu - 0u) { + // o1 = o1 + 1u; + // } + // v6 = o0; + // v7 = o1; + + // d = rotr64(d ^ a, 16) + xor0 = v28 ^ v6; + xor1 = v29 ^ v7; + v28 = (xor0 >> 16u) ^ (xor1 << 16u); + v29 = (xor1 >> 16u) ^ (xor0 << 16u); + + // c = c + d + o0 = v18 + v28; + o1 = v19 + v29; + if (v18 > 0xFFFFFFFFu - v28) { + o1 = o1 + 1u; + } + v18 = o0; + v19 = o1; + + // b = rotr64(b ^ c, 63) + xor0 = v8 ^ v18; + xor1 = v9 ^ v19; + v8 = (xor1 >> 31u) ^ (xor0 << 1u); + v9 = (xor0 >> 31u) ^ (xor1 << 1u); + + + + + + /**************************************************************************** + * ROUND(1) * + ****************************************************************************/ + + G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, 0u, 0u, 0u, 0u); + G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, m8, m9, 0u, 0u); + G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, 0u, 0u, 0u, 0u); + G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, 0u, 0u, 0u, 0u); + G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, m2, m3, 0u, 0u); + G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, m0, m1, m4, m5); + G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, 0u, 0u, 0u, 0u); + G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, 0u, 0u, m6, m7); + + + + /**************************************************************************** + * ROUND(2) * + ****************************************************************************/ + + G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, 0u, 0u, 0u, 0u); + G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, 0u, 0u, m0, m1); + G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, 0u, 0u, m4, m5); + G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, 0u, 0u, 0u, 0u); + G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, 0u, 0u, 0u, 0u); + G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, m6, m7, 0u, 0u); + G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, 0u, 0u, m2, m3); + G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, 0u, 0u, m8, m9); + + + + /**************************************************************************** + * ROUND(3) * + ****************************************************************************/ + + G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, 0u, 0u, 0u, 0u); + G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, m6, m7, m2, m3); + G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, 0u, 0u, 0u, 0u); + G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, 0u, 0u, 0u, 0u); + G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, m4, m5, 0u, 0u); + G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, 0u, 0u, 0u, 0u); + G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, m8, m9, m0, m1); + G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, 0u, 0u, 0u, 0u); + + + + /**************************************************************************** + * ROUND(4) * + ****************************************************************************/ + + G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, 0u, 0u, m0, m1); + G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, 0u, 0u, 0u, 0u); + G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, m4, m5, m8, m9); + G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, 0u, 0u, 0u, 0u); + G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, 0u, 0u, m2, m3); + G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, 0u, 0u, 0u, 0u); + G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, 0u, 0u, 0u, 0u); + G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, m6, m7, 0u, 0u); + + + + /**************************************************************************** + * ROUND(5) * + ****************************************************************************/ + + G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, m4, m5, 0u, 0u); + G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, 0u, 0u, 0u, 0u); + G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, m0, m1, 0u, 0u); + G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, 0u, 0u, m6, m7); + G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, m8, m9, 0u, 0u); + G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, 0u, 0u, 0u, 0u); + G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, 0u, 0u, 0u, 0u); + G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, m2, m3, 0u, 0u); + + + + /**************************************************************************** + * ROUND(6) * + ****************************************************************************/ + + G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, 0u, 0u, 0u, 0u); + G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, m2, m3, 0u, 0u); + G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, 0u, 0u, 0u, 0u); + G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, m8, m9, 0u, 0u); + G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, m0, m1, 0u, 0u); + G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, 0u, 0u, m6, m7); + G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, 0u, 0u, m4, m5); + G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, 0u, 0u, 0u, 0u); + + + + /**************************************************************************** + * ROUND(7) * + ****************************************************************************/ + + G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, 0u, 0u, 0u, 0u); + G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, 0u, 0u, 0u, 0u); + G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, 0u, 0u, m2, m3); + G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, m6, m7, 0u, 0u); + G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, 0u, 0u, m0, m1); + G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, 0u, 0u, m8, m9); + G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, 0u, 0u, 0u, 0u); + G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, m4, m5, 0u, 0u); + + + + /**************************************************************************** + * ROUND(8) * + ****************************************************************************/ + + G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, 0u, 0u, 0u, 0u); + G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, 0u, 0u, 0u, 0u); + G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, 0u, 0u, m6, m7); + G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, m0, m1, 0u, 0u); + G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, 0u, 0u, m4, m5); + G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, 0u, 0u, 0u, 0u); + G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, m2, m3, m8, m9); + G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, 0u, 0u, 0u, 0u); + + + + /**************************************************************************** + * ROUND(9) * + ****************************************************************************/ + + G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, 0u, 0u, m4, m5); + G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, 0u, 0u, m8, m9); + G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, 0u, 0u, 0u, 0u); + G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, m2, m3, 0u, 0u); + G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, 0u, 0u, 0u, 0u); + G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, 0u, 0u, 0u, 0u); + G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, m6, m7, 0u, 0u); + G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, 0u, 0u, m0, m1); + + + + /**************************************************************************** + * ROUND(10) * + ****************************************************************************/ + + G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, m0, m1, m2, m3); + G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, m4, m5, m6, m7); + G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, m8, m9, 0u, 0u); + G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, 0u, 0u, 0u, 0u); + G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, 0u, 0u, 0u, 0u); + G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, 0u, 0u, 0u, 0u); + G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, 0u, 0u, 0u, 0u); + G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, 0u, 0u, 0u, 0u); + + + + /**************************************************************************** + * ROUND(11) * + ****************************************************************************/ + + G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, 0u, 0u, 0u, 0u); + G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, m8, m9, 0u, 0u); + G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, 0u, 0u, 0u, 0u); + G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, 0u, 0u, 0u, 0u); + G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, m2, m3, 0u, 0u); + G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, m0, m1, m4, m5); + G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, 0u, 0u, 0u, 0u); + G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, 0u, 0u, m6, m7); + + + + /**************************************************************************** + * NONCE CHECK * + ****************************************************************************/ + + /** + * Set nonce if it passes the threshold and no other thread has set it + */ + if ((BLAKE2B_IV32_1 ^ v1 ^ v17) > threshold && atomicLoad(&work.found) == 0u) { + atomicStore(&work.found, 1u); + work.nonce.x = m0; + work.nonce.y = m1; + } + return; +} `; } }); -// dist/lib/workers/powgl.js -var PowGl, powgl_default; -var init_powgl = __esm({ - "dist/lib/workers/powgl.js"() { +// src/lib/nano-pow/shaders/gl-fragment.ts +var NanoPowGlFragmentShader; +var init_gl_fragment = __esm({ + "src/lib/nano-pow/shaders/gl-fragment.ts"() { "use strict"; - init_pool(); - PowGl = class _PowGl extends WorkerInterface { - static { - _PowGl.listen(); - } - /** - * Calculates proof-of-work as described by the Nano cryptocurrency protocol. - * - * @param {any[]} data - Array of hashes and minimum thresholds - * @returns Promise for proof-of-work attached to original array objects - */ - static async work(data) { - return new Promise(async (resolve, reject) => { - for (const d of data) { - try { - d.work = await this.search(d.hash, d.threshold); - } catch (err) { - reject(err); - } - } - resolve(data); - }); - } - // Vertex Shader - static #vsSource = `#version 300 es -#pragma vscode_glsllint_stage: vert -precision highp float; -layout (location=0) in vec4 position; -layout (location=1) in vec2 uv; - -out vec2 uv_pos; - -void main() { - uv_pos = uv; - gl_Position = position; -}`; - // Fragment shader - static #fsSource = `#version 300 es + NanoPowGlFragmentShader = `#version 300 es #pragma vscode_glsllint_stage: frag precision highp float; precision highp int; @@ -2891,7 +4545,49 @@ void main() { } else { discard; } -}`; +} +`; + } +}); + +// src/lib/nano-pow/shaders/gl-vertex.ts +var NanoPowGlVertexShader; +var init_gl_vertex = __esm({ + "src/lib/nano-pow/shaders/gl-vertex.ts"() { + "use strict"; + NanoPowGlVertexShader = `#version 300 es +#pragma vscode_glsllint_stage: vert +precision highp float; +layout (location=0) in vec4 position; +layout (location=1) in vec2 uv; + +out vec2 uv_pos; + +void main() { + uv_pos = uv; + gl_Position = position; +} +`; + } +}); + +// src/lib/nano-pow/shaders/index.ts +var init_shaders = __esm({ + "src/lib/nano-pow/shaders/index.ts"() { + "use strict"; + init_gpu_compute(); + init_gl_fragment(); + init_gl_vertex(); + } +}); + +// src/lib/nano-pow/classes/gl.ts +var NanoPowGl, gl_default; +var init_gl = __esm({ + "src/lib/nano-pow/classes/gl.ts"() { + "use strict"; + init_shaders(); + NanoPowGl = class _NanoPowGl { /** Used to set canvas size. Must be a multiple of 256. */ static #WORKLOAD = 256 * Math.max(1, Math.floor(navigator.hardwareConcurrency)); static #hexify(arr) { @@ -2911,7 +4607,7 @@ void main() { static #workBuffer; static #query; static #pixels; - // Vertex Positions, 2 triangles + /**Vertex Positions, 2 triangles */ static #positions = new Float32Array([ -1, -1, @@ -2932,7 +4628,7 @@ void main() { -1, 0 ]); - // Texture Positions + /** Texture Positions */ static #uvPosArray = new Float32Array([ 1, 1, @@ -2949,23 +4645,19 @@ void main() { ]); static { this.#gl = new OffscreenCanvas(this.#WORKLOAD, this.#WORKLOAD).getContext("webgl2"); - if (this.#gl == null) - throw new Error("WebGL 2 is required"); + if (this.#gl == null) throw new Error("WebGL 2 is required"); this.#gl.clearColor(0, 0, 0, 1); this.#program = this.#gl.createProgram(); - if (this.#program == null) - throw new Error("Failed to create shader program"); + if (this.#program == null) throw new Error("Failed to create shader program"); this.#vertexShader = this.#gl.createShader(this.#gl.VERTEX_SHADER); - if (this.#vertexShader == null) - throw new Error("Failed to create vertex shader"); - this.#gl.shaderSource(this.#vertexShader, this.#vsSource); + if (this.#vertexShader == null) throw new Error("Failed to create vertex shader"); + this.#gl.shaderSource(this.#vertexShader, NanoPowGlVertexShader); this.#gl.compileShader(this.#vertexShader); if (!this.#gl.getShaderParameter(this.#vertexShader, this.#gl.COMPILE_STATUS)) throw new Error(this.#gl.getShaderInfoLog(this.#vertexShader) ?? `Failed to compile vertex shader`); this.#fragmentShader = this.#gl.createShader(this.#gl.FRAGMENT_SHADER); - if (this.#fragmentShader == null) - throw new Error("Failed to create fragment shader"); - this.#gl.shaderSource(this.#fragmentShader, this.#fsSource); + if (this.#fragmentShader == null) throw new Error("Failed to create fragment shader"); + this.#gl.shaderSource(this.#fragmentShader, NanoPowGlFragmentShader); this.#gl.compileShader(this.#fragmentShader); if (!this.#gl.getShaderParameter(this.#fragmentShader, this.#gl.COMPILE_STATUS)) throw new Error(this.#gl.getShaderInfoLog(this.#fragmentShader) ?? `Failed to compile fragment shader`); @@ -3009,24 +4701,20 @@ void main() { * @param {number} [threshold=0xfffffff8] - Difficulty of proof-of-work calculation */ static async search(hash2, threshold = 4294967288) { - if (_PowGl.#gl == null) - throw new Error("WebGL 2 is required"); - if (!/^[A-F-a-f0-9]{64}$/.test(hash2)) - throw new Error(`invalid_hash ${hash2}`); - if (typeof threshold !== "number") - throw new TypeError(`Invalid threshold ${threshold}`); - if (this.#gl == null) - throw new Error("WebGL 2 is required"); + if (_NanoPowGl.#gl == null) throw new Error("WebGL 2 is required"); + if (!/^[A-F-a-f0-9]{64}$/.test(hash2)) throw new Error(`invalid_hash ${hash2}`); + if (typeof threshold !== "number") throw new TypeError(`Invalid threshold ${threshold}`); + if (this.#gl == null) throw new Error("WebGL 2 is required"); const uboView = new DataView(new ArrayBuffer(144)); for (let i = 0; i < 64; i += 8) { const uint32 = hash2.slice(i, i + 8); uboView.setUint32(i * 2, parseInt(uint32, 16)); } uboView.setUint32(128, threshold, true); - 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); + uboView.setFloat32(132, _NanoPowGl.#WORKLOAD - 1, true); + _NanoPowGl.#gl.bindBuffer(_NanoPowGl.#gl.UNIFORM_BUFFER, _NanoPowGl.#uboBuffer); + _NanoPowGl.#gl.bufferSubData(_NanoPowGl.#gl.UNIFORM_BUFFER, 0, uboView); + _NanoPowGl.#gl.bindBuffer(_NanoPowGl.#gl.UNIFORM_BUFFER, null); let nonce = null; const work = new Uint8Array(8); while (nonce == null) { @@ -3039,10 +4727,8 @@ void main() { return nonce; } static draw(work) { - if (this.#gl == null || this.#query == null) - throw new Error("WebGL 2 is required to draw and query pixels"); - if (this.#workBuffer == null) - throw new Error("Work buffer is required to draw"); + if (this.#gl == null || this.#query == null) throw new Error("WebGL 2 is required to draw and query pixels"); + if (this.#workBuffer == null) throw new Error("Work buffer is required to draw"); this.#gl.clear(this.#gl.COLOR_BUFFER_BIT); crypto.getRandomValues(work); this.#gl.bindBuffer(this.#gl.UNIFORM_BUFFER, this.#workBuffer); @@ -3053,15 +4739,14 @@ void main() { this.#gl.endQuery(this.#gl.ANY_SAMPLES_PASSED_CONSERVATIVE); } static async checkQueryResult() { - if (this.#gl == null || this.#query == null) - throw new Error("WebGL 2 is required to check query results"); + if (this.#gl == null || this.#query == null) throw new Error("WebGL 2 is required to check query results"); if (this.#gl.getQueryParameter(this.#query, this.#gl.QUERY_RESULT_AVAILABLE)) { return this.#gl.getQueryParameter(this.#query, this.#gl.QUERY_RESULT); } return new Promise((resolve, reject) => { try { requestAnimationFrame(async () => { - const result = await _PowGl.checkQueryResult(); + const result = await _NanoPowGl.checkQueryResult(); resolve(result); }); } catch (err) { @@ -3078,408 +4763,37 @@ void main() { * @returns Nonce as an 8-byte (16-char) hexadecimal string */ static readResult(work) { - if (this.#gl == null) - throw new Error("WebGL 2 is required to read pixels"); + if (this.#gl == null) throw new Error("WebGL 2 is required to read pixels"); this.#gl.readPixels(0, 0, this.#gl.drawingBufferWidth, this.#gl.drawingBufferHeight, this.#gl.RGBA, this.#gl.UNSIGNED_BYTE, this.#pixels); for (let i = 0; i < this.#pixels.length; i += 4) { if (this.#pixels[i] !== 0) { - const hex2 = this.#hexify(work.subarray(4, 8)) + this.#hexify([ - this.#pixels[i + 2], - this.#pixels[i + 3], - work[2] ^ this.#pixels[i] - 1, - work[3] ^ this.#pixels[i + 1] - 1 - ]); - return hex2; - } - } - throw new Error("Query reported result but nonce value not found"); - } - }; - powgl_default = ` - const WorkerInterface = ${WorkerInterface} - const PowGl = ${PowGl} -`; - } -}); - -// dist/lib/workers/powgpu.js -var PowGpu, powgpu_default; -var init_powgpu = __esm({ - "dist/lib/workers/powgpu.js"() { - "use strict"; - init_pool(); - PowGpu = class _PowGpu extends WorkerInterface { - static { - _PowGpu.listen(); - } - /** - * Calculates proof-of-work as described by the Nano cryptocurrency protocol. - * - * @param {any[]} data - Array of hashes and minimum thresholds - * @returns Promise for proof-of-work attached to original array objects - */ - static async work(data) { - return new Promise(async (resolve, reject) => { - for (const d of data) { - try { - d.work = await this.search(d.hash, d.threshold); - } catch (err) { - reject(err); - } + const hex2 = this.#hexify(work.subarray(4, 8)) + this.#hexify([ + this.#pixels[i + 2], + this.#pixels[i + 3], + work[2] ^ this.#pixels[i] - 1, + work[3] ^ this.#pixels[i + 1] - 1 + ]); + return hex2; } - resolve(data); - }); + } + throw new Error("Query reported result but nonce value not found"); } - // WebGPU Compute Shader - static shader = ` - struct UBO { - blockhash: array, 2>, - random: u32, - threshold: u32 - }; - @group(0) @binding(0) var ubo: UBO; - - struct WORK { - nonce: vec2, - found: atomic - }; - @group(0) @binding(1) var work: WORK; - - /** - * Defined separately from uint v[32] below as the original value is required - * to calculate the second uint32 of the digest for threshold comparison - */ - const BLAKE2B_IV32_1: u32 = 0x6A09E667u; - - /** - * These are offsets into the input data buffer for each mixing step. - * They are multiplied by 2 from the original SIGMA values in - * the C reference implementation, which refered to uint64s. - * - * const SIGMA82: array = array( - * 0u,2u,4u,6u,8u,10u,12u,14u,16u,18u,20u,22u,24u,26u,28u,30u, - * 28u,20u,8u,16u,18u,30u,26u,12u,2u,24u,0u,4u,22u,14u,10u,6u, - * 22u,16u,24u,0u,10u,4u,30u,26u,20u,28u,6u,12u,14u,2u,18u,8u, - * 14u,18u,6u,2u,26u,24u,22u,28u,4u,12u,10u,20u,8u,0u,30u,16u, - * 18u,0u,10u,14u,4u,8u,20u,30u,28u,2u,22u,24u,12u,16u,6u,26u, - * 4u,24u,12u,20u,0u,22u,16u,6u,8u,26u,14u,10u,30u,28u,2u,18u, - * 24u,10u,2u,30u,28u,26u,8u,20u,0u,14u,12u,6u,18u,4u,16u,22u, - * 26u,22u,14u,28u,24u,2u,6u,18u,10u,0u,30u,8u,16u,12u,4u,20u, - * 12u,30u,28u,18u,22u,6u,0u,16u,24u,4u,26u,14u,2u,8u,20u,10u, - * 20u,4u,16u,8u,14u,12u,2u,10u,30u,22u,18u,28u,6u,24u,26u,0u, - * 0u,2u,4u,6u,8u,10u,12u,14u,16u,18u,20u,22u,24u,26u,28u,30u, - * 28u,20u,8u,16u,18u,30u,26u,12u,2u,24u,0u,4u,22u,14u,10u,6u - * ); - */ - - /** - * G Mixing function - */ - fn G ( - va0: ptr, va1: ptr, - vb0: ptr, vb1: ptr, - vc0: ptr, vc1: ptr, - vd0: ptr, vd1: ptr, - mx0: u32, mx1: u32, my0: u32, my1: u32 - ) { - var o0: u32; - var o1: u32; - var xor0: u32; - var xor1: u32; - - // a = a + b; - o0 = *va0 + *vb0; - o1 = *va1 + *vb1; - if (*va0 > 0xFFFFFFFFu - *vb0) { - o1 = o1 + 1u; - } - *va0 = o0; - *va1 = o1; - - // a = a + m[sigma[r][2*i+0]]; - o0 = *va0 + mx0; - o1 = *va1 + mx1; - if (*va0 > 0xFFFFFFFFu - mx0) { - o1 = o1 + 1u; - } - *va0 = o0; - *va1 = o1; - - // d = rotr64(d ^ a, 32); - xor0 = *vd0 ^ *va0; - xor1 = *vd1 ^ *va1; - *vd0 = xor1; - *vd1 = xor0; - - // c = c + d; - o0 = *vc0 + *vd0; - o1 = *vc1 + *vd1; - if (*vc0 > 0xFFFFFFFFu - *vd0) { - o1 = o1 + 1u; - } - *vc0 = o0; - *vc1 = o1; - - // b = rotr64(b ^ c, 24); - xor0 = *vb0 ^ *vc0; - xor1 = *vb1 ^ *vc1; - *vb0 = (xor0 >> 24u) ^ (xor1 << 8u); - *vb1 = (xor1 >> 24u) ^ (xor0 << 8u); - - // a = a + b; - o0 = *va0 + *vb0; - o1 = *va1 + *vb1; - if (*va0 > 0xFFFFFFFFu - *vb0) { - o1 = o1 + 1u; - } - *va0 = o0; - *va1 = o1; - - // a = a + m[sigma[r][2*i+1]]; - o0 = *va0 + my0; - o1 = *va1 + my1; - if (*va0 > 0xFFFFFFFFu - my0) { - o1 = o1 + 1u; - } - *va0 = o0; - *va1 = o1; - - // d = rotr64(d ^ a, 16) - xor0 = *vd0 ^ *va0; - xor1 = *vd1 ^ *va1; - *vd0 = (xor0 >> 16u) ^ (xor1 << 16u); - *vd1 = (xor1 >> 16u) ^ (xor0 << 16u); - - // c = c + d; - o0 = *vc0 + *vd0; - o1 = *vc1 + *vd1; - if (*vc0 > 0xFFFFFFFFu - *vd0) { - o1 = o1 + 1u; - } - *vc0 = o0; - *vc1 = o1; - - // b = rotr64(b ^ c, 63) - xor0 = *vb0 ^ *vc0; - xor1 = *vb1 ^ *vc1; - *vb0 = (xor1 >> 31u) ^ (xor0 << 1u); - *vb1 = (xor0 >> 31u) ^ (xor1 << 1u); - } - - /** - * Main compute function - * 8-byte work is split into two 4-byte u32. Low 4 bytes are random u32 from - * UBO. High 4 bytes are the random value XOR'd with index of each thread. - */ - @compute @workgroup_size(64) - fn main( - @builtin(workgroup_id) workgroup_id: vec3, - @builtin(local_invocation_id) local_id: vec3 - ) { - if (atomicLoad(&work.found) != 0u) { return; } - - let threshold: u32 = ubo.threshold; - - /** - * Flatten 3D workgroup and local identifiers into u32 for each thread - */ - var id: u32 = ((workgroup_id.x & 0xff) << 24) | - ((workgroup_id.y & 0xff) << 16) | - ((workgroup_id.z & 0xff) << 8) | - (local_id.x & 0xff); - - /** - * Initialize (nonce||blockhash) concatenation - */ - var m0: u32 = ubo.random; - var m1: u32 = ubo.random ^ id; - var m2: u32 = ubo.blockhash[0u].x; - var m3: u32 = ubo.blockhash[0u].y; - var m4: u32 = ubo.blockhash[0u].z; - var m5: u32 = ubo.blockhash[0u].w; - var m6: u32 = ubo.blockhash[1u].x; - var m7: u32 = ubo.blockhash[1u].y; - var m8: u32 = ubo.blockhash[1u].z; - var m9: u32 = ubo.blockhash[1u].w; - - /** - * Compression buffer intialized to 2 instances of initialization vector - * The following values have been modified from the BLAKE2B_IV: - * OUTLEN is constant 8 bytes - * v[0u] ^= 0x01010000u ^ uint(OUTLEN); - * INLEN is constant 40 bytes: work value (8) + block hash (32) - * v[24u] ^= uint(INLEN); - * It is always the "last" compression at this INLEN - * v[28u] = ~v[28u]; - * v[29u] = ~v[29u]; - */ - var v0: u32 = 0xF2BDC900u; - var v1: u32 = 0x6A09E667u; - var v2: u32 = 0x84CAA73Bu; - var v3: u32 = 0xBB67AE85u; - var v4: u32 = 0xFE94F82Bu; - var v5: u32 = 0x3C6EF372u; - var v6: u32 = 0x5F1D36F1u; - var v7: u32 = 0xA54FF53Au; - var v8: u32 = 0xADE682D1u; - var v9: u32 = 0x510E527Fu; - var v10: u32 = 0x2B3E6C1Fu; - var v11: u32 = 0x9B05688Cu; - var v12: u32 = 0xFB41BD6Bu; - var v13: u32 = 0x1F83D9ABu; - var v14: u32 = 0x137E2179u; - var v15: u32 = 0x5BE0CD19u; - var v16: u32 = 0xF3BCC908u; - var v17: u32 = 0x6A09E667u; - var v18: u32 = 0x84CAA73Bu; - var v19: u32 = 0xBB67AE85u; - var v20: u32 = 0xFE94F82Bu; - var v21: u32 = 0x3C6EF372u; - var v22: u32 = 0x5F1D36F1u; - var v23: u32 = 0xA54FF53Au; - var v24: u32 = 0xADE682F9u; - var v25: u32 = 0x510E527Fu; - var v26: u32 = 0x2B3E6C1Fu; - var v27: u32 = 0x9B05688Cu; - var v28: u32 = 0x04BE4294u; - var v29: u32 = 0xE07C2654u; - var v30: u32 = 0x137E2179u; - var v31: u32 = 0x5BE0CD19u; - - /** - * Twelve rounds of mixing as part of BLAKE2b compression step - */ - // ROUND(0) - G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, m0, m1, m2, m3); - G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, m4, m5, m6, m7); - G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, m8, m9, 0u, 0u); - G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, 0u, 0u, 0u, 0u); - G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, 0u, 0u, 0u, 0u); - G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, 0u, 0u, 0u, 0u); - G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, 0u, 0u, 0u, 0u); - G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, 0u, 0u, 0u, 0u); - - // ROUND(1) - G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, 0u, 0u, 0u, 0u); - G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, m8, m9, 0u, 0u); - G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, 0u, 0u, 0u, 0u); - G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, 0u, 0u, 0u, 0u); - G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, m2, m3, 0u, 0u); - G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, m0, m1, m4, m5); - G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, 0u, 0u, 0u, 0u); - G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, 0u, 0u, m6, m7); - - // ROUND(2) - G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, 0u, 0u, 0u, 0u); - G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, 0u, 0u, m0, m1); - G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, 0u, 0u, m4, m5); - G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, 0u, 0u, 0u, 0u); - G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, 0u, 0u, 0u, 0u); - G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, m6, m7, 0u, 0u); - G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, 0u, 0u, m2, m3); - G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, 0u, 0u, m8, m9); - - // ROUND(3) - G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, 0u, 0u, 0u, 0u); - G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, m6, m7, m2, m3); - G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, 0u, 0u, 0u, 0u); - G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, 0u, 0u, 0u, 0u); - G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, m4, m5, 0u, 0u); - G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, 0u, 0u, 0u, 0u); - G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, m8, m9, m0, m1); - G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, 0u, 0u, 0u, 0u); - - // ROUND(4) - G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, 0u, 0u, m0, m1); - G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, 0u, 0u, 0u, 0u); - G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, m4, m5, m8, m9); - G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, 0u, 0u, 0u, 0u); - G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, 0u, 0u, m2, m3); - G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, 0u, 0u, 0u, 0u); - G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, 0u, 0u, 0u, 0u); - G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, m6, m7, 0u, 0u); - - // ROUND(5) - G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, m4, m5, 0u, 0u); - G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, 0u, 0u, 0u, 0u); - G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, m0, m1, 0u, 0u); - G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, 0u, 0u, m6, m7); - G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, m8, m9, 0u, 0u); - G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, 0u, 0u, 0u, 0u); - G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, 0u, 0u, 0u, 0u); - G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, m2, m3, 0u, 0u); - - // ROUND(6) - G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, 0u, 0u, 0u, 0u); - G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, m2, m3, 0u, 0u); - G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, 0u, 0u, 0u, 0u); - G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, m8, m9, 0u, 0u); - G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, m0, m1, 0u, 0u); - G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, 0u, 0u, m6, m7); - G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, 0u, 0u, m4, m5); - G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, 0u, 0u, 0u, 0u); - - // ROUND(7) - G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, 0u, 0u, 0u, 0u); - G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, 0u, 0u, 0u, 0u); - G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, 0u, 0u, m2, m3); - G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, m6, m7, 0u, 0u); - G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, 0u, 0u, m0, m1); - G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, 0u, 0u, m8, m9); - G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, 0u, 0u, 0u, 0u); - G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, m4, m5, 0u, 0u); - - // ROUND(8) - G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, 0u, 0u, 0u, 0u); - G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, 0u, 0u, 0u, 0u); - G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, 0u, 0u, m6, m7); - G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, m0, m1, 0u, 0u); - G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, 0u, 0u, m4, m5); - G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, 0u, 0u, 0u, 0u); - G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, m2, m3, m8, m9); - G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, 0u, 0u, 0u, 0u); - - // ROUND(9) - G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, 0u, 0u, m4, m5); - G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, 0u, 0u, m8, m9); - G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, 0u, 0u, 0u, 0u); - G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, m2, m3, 0u, 0u); - G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, 0u, 0u, 0u, 0u); - G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, 0u, 0u, 0u, 0u); - G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, m6, m7, 0u, 0u); - G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, 0u, 0u, m0, m1); - - // ROUND(10) - G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, m0, m1, m2, m3); - G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, m4, m5, m6, m7); - G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, m8, m9, 0u, 0u); - G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, 0u, 0u, 0u, 0u); - G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, 0u, 0u, 0u, 0u); - G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, 0u, 0u, 0u, 0u); - G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, 0u, 0u, 0u, 0u); - G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, 0u, 0u, 0u, 0u); - - // ROUND(11) - G(&v0, &v1, &v8, &v9, &v16, &v17, &v24, &v25, 0u, 0u, 0u, 0u); - G(&v2, &v3, &v10, &v11, &v18, &v19, &v26, &v27, m8, m9, 0u, 0u); - G(&v4, &v5, &v12, &v13, &v20, &v21, &v28, &v29, 0u, 0u, 0u, 0u); - G(&v6, &v7, &v14, &v15, &v22, &v23, &v30, &v31, 0u, 0u, 0u, 0u); - G(&v0, &v1, &v10, &v11, &v20, &v21, &v30, &v31, m2, m3, 0u, 0u); - G(&v2, &v3, &v12, &v13, &v22, &v23, &v24, &v25, m0, m1, m4, m5); - G(&v4, &v5, &v14, &v15, &v16, &v17, &v26, &v27, 0u, 0u, 0u, 0u); - G(&v6, &v7, &v8, &v9, &v18, &v19, &v28, &v29, 0u, 0u, m6, m7); - - /** - * Set nonce if it passes the threshold and no other thread has set it - */ - if ((BLAKE2B_IV32_1 ^ v1 ^ v17) > threshold && atomicLoad(&work.found) == 0u) { - atomicStore(&work.found, 1u); - work.nonce.x = m0; - work.nonce.y = m1; - } - return; - } - `; + }; + gl_default = ` + const NanoPowGlFragmentShader = \`${NanoPowGlFragmentShader}\` + const NanoPowGlVertexShader = \`${NanoPowGlVertexShader}\` + const PowGl = ${NanoPowGl} +`; + } +}); + +// src/lib/nano-pow/classes/gpu.ts +var NanoPowGpu, gpu_default; +var init_gpu = __esm({ + "src/lib/nano-pow/classes/gpu.ts"() { + "use strict"; + init_shaders(); + NanoPowGpu = class { // Initialize WebGPU static #device = null; static #uboBuffer; @@ -3542,7 +4856,7 @@ var init_powgpu = __esm({ compute: { entryPoint: "main", module: this.#device.createShaderModule({ - code: this.shader + code: NanoPowGpuComputeShader }) } }); @@ -3558,17 +4872,14 @@ var init_powgpu = __esm({ * @param {number} [threshold=0xfffffff8] - Difficulty of proof-of-work calculation */ static async search(hash2, threshold = 4294967288) { - if (!/^[A-Fa-f0-9]{64}$/.test(hash2)) - throw new TypeError(`Invalid hash ${hash2}`); - if (typeof threshold !== "number") - throw new TypeError(`Invalid threshold ${threshold}`); + if (!/^[A-Fa-f0-9]{64}$/.test(hash2)) throw new TypeError(`Invalid hash ${hash2}`); + if (typeof threshold !== "number") throw new TypeError(`Invalid threshold ${threshold}`); while (this.#device == null && performance.now() < 8e3) { await new Promise((resolve) => { setTimeout(resolve, 500); }); } - if (this.#device == null) - throw new Error(`WebGPU device failed to load.`); + if (this.#device == null) throw new Error(`WebGPU device failed to load.`); const uboView = new DataView(new ArrayBuffer(48)); for (let i = 0; i < 64; i += 8) { const uint32 = hash2.slice(i, i + 8); @@ -3602,7 +4913,13 @@ var init_powgpu = __esm({ passEncoder.setBindGroup(0, bindGroup); passEncoder.dispatchWorkgroups(256, 256, 256); passEncoder.end(); - commandEncoder.copyBufferToBuffer(this.#gpuBuffer, 0, this.#cpuBuffer, 0, 12); + commandEncoder.copyBufferToBuffer( + this.#gpuBuffer, + 0, + this.#cpuBuffer, + 0, + 12 + ); this.#device.queue.submit([commandEncoder.finish()]); try { await this.#cpuBuffer.mapAsync(GPUMapMode.READ); @@ -3623,21 +4940,72 @@ var init_powgpu = __esm({ } } }; - powgpu_default = ` + gpu_default = ` + const NanoPowGpuComputeShader = \`${NanoPowGpuComputeShader}\` + const NanoPowGpu = ${NanoPowGpu} +`; + } +}); + +// src/lib/nano-pow/classes/index.ts +var init_classes = __esm({ + "src/lib/nano-pow/classes/index.ts"() { + "use strict"; + init_gl(); + init_gpu(); + } +}); + +// src/lib/workers/nano-pow.ts +var NanoPow, nano_pow_default; +var init_nano_pow = __esm({ + "src/lib/workers/nano-pow.ts"() { + "use strict"; + init_pool(); + init_shaders(); + init_classes(); + NanoPow = class _NanoPow extends WorkerInterface { + static { + _NanoPow.listen(); + } + /** + * Calculates proof-of-work as described by the Nano cryptocurrency protocol. + * + * @param {any[]} data - Array of hashes and minimum thresholds + * @returns Promise for proof-of-work attached to original array objects + */ + static async work(data) { + return new Promise(async (resolve, reject) => { + for (const d of data) { + try { + d.work = await NanoPowGpu.search(d.hash, d.threshold); + } catch (err) { + reject(err); + } + } + resolve(data); + }); + } + }; + nano_pow_default = ` + const NanoPowGpuComputeShader = \`${NanoPowGpuComputeShader}\` + const NanoPowGlFragmentShader = \`${NanoPowGlFragmentShader}\` + const NanoPowGlVertexShader = \`${NanoPowGlVertexShader}\` + const NanoPowGl = ${NanoPowGl} + const NanoPowGpu = ${NanoPowGpu} const WorkerInterface = ${WorkerInterface} - const PowGpu = ${PowGpu} + const NanoPow = ${NanoPow} `; } }); -// dist/lib/workers.js +// src/lib/workers/index.ts var init_workers = __esm({ - "dist/lib/workers.js"() { + "src/lib/workers/index.ts"() { "use strict"; init_bip44_ckd(); init_nano_nacl(); - init_powgl(); - init_powgpu(); + init_nano_pow(); } }); @@ -9638,13 +11006,252 @@ var init_TransportWebUSB = __esm({ step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; - configurationValue = 1; - endpointNumber = 3; - TransportWebUSB = class _TransportWebUSB extends Transport_default { - constructor(device, interfaceNumber) { + configurationValue = 1; + endpointNumber = 3; + TransportWebUSB = class _TransportWebUSB extends Transport_default { + constructor(device, interfaceNumber) { + super(); + this.channel = Math.floor(Math.random() * 65535); + this.packetSize = 64; + this._disconnectEmitted = false; + this._emitDisconnect = (e) => { + if (this._disconnectEmitted) + return; + this._disconnectEmitted = true; + this.emit("disconnect", e); + }; + this.device = device; + this.interfaceNumber = interfaceNumber; + this.deviceModel = identifyUSBProductId(device.productId); + } + /** + * Similar to create() except it will always display the device permission (even if some devices are already accepted). + */ + static request() { + return __awaiter6(this, void 0, void 0, function* () { + const device = yield requestLedgerDevice(); + return _TransportWebUSB.open(device); + }); + } + /** + * Similar to create() except it will never display the device permission (it returns a Promise, null if it fails to find a device). + */ + static openConnected() { + return __awaiter6(this, void 0, void 0, function* () { + const devices2 = yield getLedgerDevices(); + if (devices2.length === 0) + return null; + return _TransportWebUSB.open(devices2[0]); + }); + } + /** + * Create a Ledger transport with a USBDevice + */ + static open(device) { + return __awaiter6(this, void 0, void 0, function* () { + yield device.open(); + if (device.configuration === null) { + yield device.selectConfiguration(configurationValue); + } + yield gracefullyResetDevice(device); + const iface = device.configurations[0].interfaces.find(({ alternates }) => alternates.some((a) => a.interfaceClass === 255)); + if (!iface) { + throw new TransportInterfaceNotAvailable("No WebUSB interface found for your Ledger device. Please upgrade firmware or contact techsupport."); + } + const interfaceNumber = iface.interfaceNumber; + try { + yield device.claimInterface(interfaceNumber); + } catch (e) { + yield device.close(); + throw new TransportInterfaceNotAvailable(e.message); + } + const transport = new _TransportWebUSB(device, interfaceNumber); + const onDisconnect = (e) => { + if (device === e.device) { + navigator.usb.removeEventListener("disconnect", onDisconnect); + transport._emitDisconnect(new DisconnectedDevice()); + } + }; + navigator.usb.addEventListener("disconnect", onDisconnect); + return transport; + }); + } + /** + * Release the transport device + */ + close() { + return __awaiter6(this, void 0, void 0, function* () { + yield this.exchangeBusyPromise; + yield this.device.releaseInterface(this.interfaceNumber); + yield gracefullyResetDevice(this.device); + yield this.device.close(); + }); + } + /** + * Exchange with the device using APDU protocol. + * @param apdu + * @returns a promise of apdu response + */ + exchange(apdu) { + return __awaiter6(this, void 0, void 0, function* () { + const b = yield this.exchangeAtomicImpl(() => __awaiter6(this, void 0, void 0, function* () { + const { channel, packetSize } = this; + log("apdu", "=> " + apdu.toString("hex")); + const framing = hid_framing_default(channel, packetSize); + const blocks = framing.makeBlocks(apdu); + for (let i = 0; i < blocks.length; i++) { + yield this.device.transferOut(endpointNumber, blocks[i]); + } + let result; + let acc; + while (!(result = framing.getReducedResult(acc))) { + const r = yield this.device.transferIn(endpointNumber, packetSize); + const buffer2 = Buffer.from(r.data.buffer); + acc = framing.reduceResponse(acc, buffer2); + } + log("apdu", "<= " + result.toString("hex")); + return result; + })).catch((e) => { + if (e && e.message && e.message.includes("disconnected")) { + this._emitDisconnect(e); + throw new DisconnectedDeviceDuringOperation(e.message); + } + throw e; + }); + return b; + }); + } + setScrambleKey() { + } + }; + TransportWebUSB.isSupported = isSupported; + TransportWebUSB.list = getLedgerDevices; + TransportWebUSB.listen = (observer) => { + let unsubscribed = false; + getFirstLedgerDevice().then((device) => { + if (!unsubscribed) { + const deviceModel = identifyUSBProductId(device.productId); + observer.next({ + type: "add", + descriptor: device, + deviceModel + }); + observer.complete(); + } + }, (error) => { + if (window.DOMException && error instanceof window.DOMException && error.code === 18) { + observer.error(new TransportWebUSBGestureRequired(error.message)); + } else { + observer.error(new TransportOpenUserCancelled(error.message)); + } + }); + function unsubscribe() { + unsubscribed = true; + } + return { + unsubscribe + }; + }; + TransportWebUSB_default = TransportWebUSB; + } +}); + +// node_modules/@ledgerhq/hw-transport-webhid/lib-es/TransportWebHID.js +function requestLedgerDevices() { + return __awaiter7(this, void 0, void 0, function* () { + const device = yield getHID().requestDevice({ + filters: ledgerDevices2 + }); + if (Array.isArray(device)) + return device; + return [device]; + }); +} +function getLedgerDevices2() { + return __awaiter7(this, void 0, void 0, function* () { + const devices2 = yield getHID().getDevices(); + return devices2.filter((d) => d.vendorId === ledgerUSBVendorId); + }); +} +function getFirstLedgerDevice2() { + return __awaiter7(this, void 0, void 0, function* () { + const existingDevices = yield getLedgerDevices2(); + if (existingDevices.length > 0) + return existingDevices[0]; + const devices2 = yield requestLedgerDevices(); + return devices2[0]; + }); +} +var __awaiter7, ledgerDevices2, isSupported2, getHID, TransportWebHID, TransportWebHID_default; +var init_TransportWebHID = __esm({ + "node_modules/@ledgerhq/hw-transport-webhid/lib-es/TransportWebHID.js"() { + init_Transport(); + init_hid_framing(); + init_lib_es3(); + init_lib_es2(); + init_lib_es(); + __awaiter7 = function(thisArg, _arguments, P, generator) { + function adopt(value) { + return value instanceof P ? value : new P(function(resolve) { + resolve(value); + }); + } + return new (P || (P = Promise))(function(resolve, reject) { + function fulfilled(value) { + try { + step(generator.next(value)); + } catch (e) { + reject(e); + } + } + function rejected(value) { + try { + step(generator["throw"](value)); + } catch (e) { + reject(e); + } + } + function step(result) { + result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); + } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); + }; + ledgerDevices2 = [ + { + vendorId: ledgerUSBVendorId + } + ]; + isSupported2 = () => Promise.resolve(!!(window.navigator && window.navigator.hid)); + getHID = () => { + const { hid } = navigator; + if (!hid) + throw new TransportError("navigator.hid is not supported", "HIDNotSupported"); + return hid; + }; + TransportWebHID = class _TransportWebHID extends Transport_default { + constructor(device) { super(); this.channel = Math.floor(Math.random() * 65535); this.packetSize = 64; + this.inputs = []; + this.read = () => { + if (this.inputs.length) { + return Promise.resolve(this.inputs.shift()); + } + return new Promise((success) => { + this.inputCallback = success; + }); + }; + this.onInputReport = (e) => { + const buffer2 = Buffer.from(e.data.buffer); + if (this.inputCallback) { + this.inputCallback(buffer2); + this.inputCallback = null; + } else { + this.inputs.push(buffer2); + } + }; this._disconnectEmitted = false; this._emitDisconnect = (e) => { if (this._disconnectEmitted) @@ -9652,907 +11259,1559 @@ var init_TransportWebUSB = __esm({ this._disconnectEmitted = true; this.emit("disconnect", e); }; + this.exchange = (apdu) => __awaiter7(this, void 0, void 0, function* () { + const b = yield this.exchangeAtomicImpl(() => __awaiter7(this, void 0, void 0, function* () { + const { channel, packetSize } = this; + log("apdu", "=> " + apdu.toString("hex")); + const framing = hid_framing_default(channel, packetSize); + const blocks = framing.makeBlocks(apdu); + for (let i = 0; i < blocks.length; i++) { + yield this.device.sendReport(0, blocks[i]); + } + let result; + let acc; + while (!(result = framing.getReducedResult(acc))) { + const buffer2 = yield this.read(); + acc = framing.reduceResponse(acc, buffer2); + } + log("apdu", "<= " + result.toString("hex")); + return result; + })).catch((e) => { + if (e && e.message && e.message.includes("write")) { + this._emitDisconnect(e); + throw new DisconnectedDeviceDuringOperation(e.message); + } + throw e; + }); + return b; + }); this.device = device; - this.interfaceNumber = interfaceNumber; - this.deviceModel = identifyUSBProductId(device.productId); + this.deviceModel = typeof device.productId === "number" ? identifyUSBProductId(device.productId) : void 0; + device.addEventListener("inputreport", this.onInputReport); } /** * Similar to create() except it will always display the device permission (even if some devices are already accepted). */ static request() { - return __awaiter6(this, void 0, void 0, function* () { - const device = yield requestLedgerDevice(); - return _TransportWebUSB.open(device); + return __awaiter7(this, void 0, void 0, function* () { + const [device] = yield requestLedgerDevices(); + return _TransportWebHID.open(device); }); } /** * Similar to create() except it will never display the device permission (it returns a Promise, null if it fails to find a device). */ static openConnected() { - return __awaiter6(this, void 0, void 0, function* () { - const devices2 = yield getLedgerDevices(); + return __awaiter7(this, void 0, void 0, function* () { + const devices2 = yield getLedgerDevices2(); if (devices2.length === 0) return null; - return _TransportWebUSB.open(devices2[0]); + return _TransportWebHID.open(devices2[0]); + }); + } + /** + * Create a Ledger transport with a HIDDevice + */ + static open(device) { + return __awaiter7(this, void 0, void 0, function* () { + yield device.open(); + const transport = new _TransportWebHID(device); + const onDisconnect = (e) => { + if (device === e.device) { + getHID().removeEventListener("disconnect", onDisconnect); + transport._emitDisconnect(new DisconnectedDevice()); + } + }; + getHID().addEventListener("disconnect", onDisconnect); + return transport; + }); + } + /** + * Release the transport device + */ + close() { + return __awaiter7(this, void 0, void 0, function* () { + yield this.exchangeBusyPromise; + this.device.removeEventListener("inputreport", this.onInputReport); + yield this.device.close(); }); } + setScrambleKey() { + } + }; + TransportWebHID.isSupported = isSupported2; + TransportWebHID.list = getLedgerDevices2; + TransportWebHID.listen = (observer) => { + let unsubscribed = false; + getFirstLedgerDevice2().then((device) => { + if (!device) { + observer.error(new TransportOpenUserCancelled("Access denied to use Ledger device")); + } else if (!unsubscribed) { + const deviceModel = typeof device.productId === "number" ? identifyUSBProductId(device.productId) : void 0; + observer.next({ + type: "add", + descriptor: device, + deviceModel + }); + observer.complete(); + } + }, (error) => { + observer.error(new TransportOpenUserCancelled(error.message)); + }); + function unsubscribe() { + unsubscribed = true; + } + return { + unsubscribe + }; + }; + TransportWebHID_default = TransportWebHID; + } +}); + +// dist/lib/ledger.js +var ledger_exports = {}; +__export(ledger_exports, { + Ledger: () => Ledger +}); +var Ledger; +var init_ledger = __esm({ + "dist/lib/ledger.js"() { + "use strict"; + init_TransportWebBLE(); + init_TransportWebUSB(); + init_TransportWebHID(); + init_constants(); + init_convert(); + init_rpc(); + init_block(); + Ledger = class _Ledger { + static #isInternal = false; + #status = "DISCONNECTED"; + get status() { + return this.#status; + } + openTimeout = 3e3; + listenTimeout = 3e4; + transport = null; + DynamicTransport = TransportWebHID_default; + constructor() { + if (!_Ledger.#isInternal) { + throw new Error("Ledger cannot be instantiated directly. Use Ledger.init()"); + } + _Ledger.#isInternal = false; + } + static async init() { + _Ledger.#isInternal = true; + const self = new this(); + await self.checkBrowserSupport(); + await self.listen(); + return self; + } + /** + * Check which transport protocols are supported by the browser and set the + * transport type according to the following priorities: Bluetooth, USB, HID. + */ + async checkBrowserSupport() { + console.log("Checking browser Ledger support..."); + const supports = { + ble: await TransportWebBLE_default.isSupported(), + usb: await TransportWebUSB_default.isSupported(), + hid: await TransportWebHID_default.isSupported() + }; + console.log(`ble: ${supports.ble}; usb: ${supports.usb}; hid: ${supports.hid}`); + if (supports.ble) { + this.DynamicTransport = TransportWebBLE_default; + } else if (supports.usb) { + this.DynamicTransport = TransportWebUSB_default; + } else if (supports.hid) { + this.DynamicTransport = TransportWebHID_default; + } else { + throw new Error("Unsupported browser"); + } + } + async listen() { + const { usb } = globalThis.navigator; + if (usb) { + usb.addEventListener("connect", console.log.bind(console)); + usb.addEventListener("disconnect", console.log.bind(console)); + } + } + async connect() { + const { usb } = globalThis.navigator; + if (usb) { + usb.removeEventListener("disconnect", this.onDisconnectUsb.bind(this)); + usb.addEventListener("disconnect", this.onDisconnectUsb.bind(this)); + } + const version = await this.version(); + if (version.status === "OK") { + if (version.name === "Nano") { + const account = await this.account(); + if (account.status === "OK") { + this.#status = "CONNECTED"; + } else if (account.status === "SECURITY_STATUS_NOT_SATISFIED") { + this.#status = "LOCKED"; + } else { + this.#status = "DISCONNECTED"; + } + } else if (version.name === "BOLOS") { + const open2 = await this.open(); + this.#status = open2.status === "OK" ? "CONNECTED" : "DISCONNECTED"; + } else { + this.#status = "BUSY"; + } + } else { + this.#status = "DISCONNECTED"; + } + return this.status; + } + async onDisconnectUsb(e) { + if (e.device?.manufacturerName === "Ledger") { + const { usb } = globalThis.navigator; + usb.removeEventListener("disconnect", this.onDisconnectUsb); + this.#status = "DISCONNECTED"; + } + } + /** + * Open Nano app by launching user flow. + * + * https://developers.ledger.com/docs/connectivity/ledgerJS/open-close-info-on-apps#open-application + * + * This command resets the internal USB connection of the device which can + * cause subsequent commands to fail if called too quickly. A one-second delay + * is implemented in this method to mitigate the issue. + * + * https://github.com/LedgerHQ/ledger-live/issues/4964#issuecomment-1878361157 + * + * @returns Status of command + */ + async open() { + const name = new TextEncoder().encode("Nano"); + const transport = await this.DynamicTransport.create(this.openTimeout, this.listenTimeout); + const response = await transport.send(224, 216, 0, 0, name).then((res) => bytes.toDec(res)).catch((err) => err.statusCode); + return new Promise((resolve) => setTimeout(resolve, 1e3, { status: LEDGER_STATUS_CODES[response] })); + } /** - * Create a Ledger transport with a USBDevice - */ - static open(device) { - return __awaiter6(this, void 0, void 0, function* () { - yield device.open(); - if (device.configuration === null) { - yield device.selectConfiguration(configurationValue); - } - yield gracefullyResetDevice(device); - const iface = device.configurations[0].interfaces.find(({ alternates }) => alternates.some((a) => a.interfaceClass === 255)); - if (!iface) { - throw new TransportInterfaceNotAvailable("No WebUSB interface found for your Ledger device. Please upgrade firmware or contact techsupport."); - } - const interfaceNumber = iface.interfaceNumber; - try { - yield device.claimInterface(interfaceNumber); - } catch (e) { - yield device.close(); - throw new TransportInterfaceNotAvailable(e.message); - } - const transport = new _TransportWebUSB(device, interfaceNumber); - const onDisconnect = (e) => { - if (device === e.device) { - navigator.usb.removeEventListener("disconnect", onDisconnect); - transport._emitDisconnect(new DisconnectedDevice()); - } - }; - navigator.usb.addEventListener("disconnect", onDisconnect); - return transport; - }); + * Close the currently running app. + * + * https://developers.ledger.com/docs/connectivity/ledgerJS/open-close-info-on-apps#quit-application + * + * This command resets the internal USB connection of the device which can + * cause subsequent commands to fail if called too quickly. A one-second delay + * is implemented in this method to mitigate the issue. + * + * https://github.com/LedgerHQ/ledger-live/issues/4964#issuecomment-1878361157 + * + * @returns Status of command + */ + async close() { + const transport = await this.DynamicTransport.create(this.openTimeout, this.listenTimeout); + const response = await transport.send(176, 167, 0, 0).then((res) => bytes.toDec(res)).catch((err) => err.statusCode); + return new Promise((resolve) => setTimeout(resolve, 1e3, { status: LEDGER_STATUS_CODES[response] })); } /** - * Release the transport device - */ - close() { - return __awaiter6(this, void 0, void 0, function* () { - yield this.exchangeBusyPromise; - yield this.device.releaseInterface(this.interfaceNumber); - yield gracefullyResetDevice(this.device); - yield this.device.close(); - }); + * Get the version of the current process. If a specific app is running, get + * the app version. Otherwise, get the Ledger BOLOS version instead. + * + * https://developers.ledger.com/docs/connectivity/ledgerJS/open-close-info-on-apps#get-information + * + * @returns Status, process name, and version + */ + async version() { + const transport = await this.DynamicTransport.create(this.openTimeout, this.listenTimeout); + const response = await transport.send(176, 1, 0, 0).catch((err) => dec.toBytes(err.statusCode)); + await transport.close(); + if (response.length === 2) { + const statusCode2 = bytes.toDec(response); + const status2 = LEDGER_STATUS_CODES[statusCode2] ?? "UNKNOWN_ERROR"; + return { status: status2, name: null, version: null }; + } + const nameLength = response[1]; + const name = response.slice(2, 2 + nameLength).toString(); + const versionLength = response[2 + nameLength]; + const version = response.slice(2 + nameLength + 1, 2 + nameLength + 1 + versionLength).toString(); + const statusCode = bytes.toDec(response.slice(-2)); + const status = LEDGER_STATUS_CODES[statusCode]; + return { status, name, version }; } /** - * Exchange with the device using APDU protocol. - * @param apdu - * @returns a promise of apdu response - */ - exchange(apdu) { - return __awaiter6(this, void 0, void 0, function* () { - const b = yield this.exchangeAtomicImpl(() => __awaiter6(this, void 0, void 0, function* () { - const { channel, packetSize } = this; - log("apdu", "=> " + apdu.toString("hex")); - const framing = hid_framing_default(channel, packetSize); - const blocks = framing.makeBlocks(apdu); - for (let i = 0; i < blocks.length; i++) { - yield this.device.transferOut(endpointNumber, blocks[i]); - } - let result; - let acc; - while (!(result = framing.getReducedResult(acc))) { - const r = yield this.device.transferIn(endpointNumber, packetSize); - const buffer2 = Buffer.from(r.data.buffer); - acc = framing.reduceResponse(acc, buffer2); - } - log("apdu", "<= " + result.toString("hex")); - return result; - })).catch((e) => { - if (e && e.message && e.message.includes("disconnected")) { - this._emitDisconnect(e); - throw new DisconnectedDeviceDuringOperation(e.message); - } - throw e; - }); - return b; - }); + * Get an account at a specific BIP-44 index. + * + * @returns Response object containing command status, public key, and address + */ + async account(index = 0, show = false) { + if (typeof index !== "number" || index < 0 || index >= HARDENED_OFFSET) { + throw new TypeError("Invalid account index"); + } + const purpose = dec.toBytes(BIP44_PURPOSE + HARDENED_OFFSET, 4); + const coin = dec.toBytes(BIP44_COIN_NANO + HARDENED_OFFSET, 4); + const account = dec.toBytes(index + HARDENED_OFFSET, 4); + const data = new Uint8Array([3, ...purpose, ...coin, ...account]); + const transport = await this.DynamicTransport.create(this.openTimeout, this.listenTimeout); + const response = await transport.send(161, 2, show ? 1 : 0, 0, data).catch((err) => dec.toBytes(err.statusCode)); + await transport.close(); + if (response.length === 2) { + const statusCode = bytes.toDec(response); + const status = LEDGER_STATUS_CODES[statusCode] ?? "UNKNOWN_ERROR"; + return { status, publicKey: null, address: null }; + } + try { + const publicKey = bytes.toHex(response.slice(0, 32)); + const addressLength = response[32]; + const address = response.slice(33, 33 + addressLength).toString(); + const statusCode = bytes.toDec(response.slice(33 + addressLength)); + const status = LEDGER_STATUS_CODES[statusCode]; + return { status, publicKey, address }; + } catch (err) { + return { status: "ERROR_PARSING_ACCOUNT", publicKey: null, address: null }; + } } - setScrambleKey() { + /** + * Cache frontier block in device memory. + * + * @param {number} index - Account number + * @param {any} block - Block data to cache + * @returns Status of command + */ + async cacheBlock(index = 0, block) { + if (typeof index !== "number" || index < 0 || index >= HARDENED_OFFSET) { + throw new TypeError("Invalid account index"); + } + if (!(block instanceof SendBlock) && !(block instanceof ReceiveBlock) && !(block instanceof ChangeBlock)) { + throw new TypeError("Invalid block format"); + } + if (!block.signature) { + throw new ReferenceError("Cannot cache unsigned block"); + } + const purpose = dec.toBytes(BIP44_PURPOSE + HARDENED_OFFSET, 4); + const coin = dec.toBytes(BIP44_COIN_NANO + HARDENED_OFFSET, 4); + const account = dec.toBytes(index + HARDENED_OFFSET, 4); + const previous = hex.toBytes(block.previous); + const link = hex.toBytes(block.link); + const representative = hex.toBytes(block.representative.publicKey); + const balance = hex.toBytes(BigInt(block.balance).toString(16), 16); + const signature = hex.toBytes(block.signature); + const data = new Uint8Array([3, ...purpose, ...coin, ...account, ...previous, ...link, ...representative, ...balance, ...signature]); + const transport = await this.DynamicTransport.create(this.openTimeout, this.listenTimeout); + const response = await transport.send(161, 3, 0, 0, data).then((res) => bytes.toDec(res)).catch((err) => err.statusCode); + await transport.close(); + return { status: LEDGER_STATUS_CODES[response] }; } - }; - TransportWebUSB.isSupported = isSupported; - TransportWebUSB.list = getLedgerDevices; - TransportWebUSB.listen = (observer) => { - let unsubscribed = false; - getFirstLedgerDevice().then((device) => { - if (!unsubscribed) { - const deviceModel = identifyUSBProductId(device.productId); - observer.next({ - type: "add", - descriptor: device, - deviceModel - }); - observer.complete(); + async sign(index = 0, input) { + if (typeof index !== "number" || index < 0 || index >= HARDENED_OFFSET) { + throw new TypeError("Invalid account index"); } - }, (error) => { - if (window.DOMException && error instanceof window.DOMException && error.code === 18) { - observer.error(new TransportWebUSBGestureRequired(error.message)); + const purpose = dec.toBytes(BIP44_PURPOSE + HARDENED_OFFSET, 4); + const coin = dec.toBytes(BIP44_COIN_NANO + HARDENED_OFFSET, 4); + const account = dec.toBytes(index + HARDENED_OFFSET, 4); + const transport = await this.DynamicTransport.create(this.openTimeout, this.listenTimeout); + if (typeof input === "string") { + const nonce = utf8.toBytes(input); + if (nonce.length !== 16) { + throw new RangeError("Nonce must be 16-byte string"); + } + const data = new Uint8Array([3, ...purpose, ...coin, ...account, ...nonce]); + const response = await transport.send(161, 5, 0, 0, data).catch((err) => dec.toBytes(err.statusCode)); + await transport.close(); + if (response.length === 2) { + const statusCode2 = bytes.toDec(response); + const status2 = LEDGER_STATUS_CODES[statusCode2] ?? "UNKNOWN_ERROR"; + return { status: status2, signature: null }; + } + const signature = bytes.toHex(response.slice(0, 64)); + const statusCode = bytes.toDec(response.slice(-2)); + const status = LEDGER_STATUS_CODES[statusCode]; + return { status, signature }; } else { - observer.error(new TransportOpenUserCancelled(error.message)); + const previous = hex.toBytes(input.previous); + const link = hex.toBytes(input.link); + const representative = hex.toBytes(input.representative.publicKey); + const balance = hex.toBytes(BigInt(input.balance).toString(16), 16); + const data = new Uint8Array([3, ...purpose, ...coin, ...account, ...previous, ...link, ...representative, ...balance]); + const response = await transport.send(161, 4, 0, 0, data).catch((err) => dec.toBytes(err.statusCode)); + await transport.close(); + if (response.length === 2) { + const statusCode2 = bytes.toDec(response); + const status2 = LEDGER_STATUS_CODES[statusCode2] ?? "UNKNOWN_ERROR"; + return { status: status2, signature: null, hash: null }; + } + const hash2 = bytes.toHex(response.slice(0, 32)); + const signature = bytes.toHex(response.slice(32, 96)); + const statusCode = bytes.toDec(response.slice(-2)); + const status = LEDGER_STATUS_CODES[statusCode]; + return { status, signature, hash: hash2 }; } - }); - function unsubscribe() { - unsubscribed = true; } - return { - unsubscribe - }; + async updateCache(index, input, node) { + if (typeof input === "string" && node?.constructor === Rpc) { + const data = { + "json_block": "true", + "hash": input + }; + const res = await node.call("block_info", data); + if (!res || res.ok === false) { + throw new Error(`Unable to fetch block info`, res); + } + input = res.contents; + } + return this.cacheBlock(index, input); + } }; - TransportWebUSB_default = TransportWebUSB; } }); -// node_modules/@ledgerhq/hw-transport-webhid/lib-es/TransportWebHID.js -function requestLedgerDevices() { - return __awaiter7(this, void 0, void 0, function* () { - const device = yield getHID().requestDevice({ - filters: ledgerDevices2 - }); - if (Array.isArray(device)) - return device; - return [device]; - }); -} -function getLedgerDevices2() { - return __awaiter7(this, void 0, void 0, function* () { - const devices2 = yield getHID().getDevices(); - return devices2.filter((d) => d.vendorId === ledgerUSBVendorId); - }); -} -function getFirstLedgerDevice2() { - return __awaiter7(this, void 0, void 0, function* () { - const existingDevices = yield getLedgerDevices2(); - if (existingDevices.length > 0) - return existingDevices[0]; - const devices2 = yield requestLedgerDevices(); - return devices2[0]; - }); -} -var __awaiter7, ledgerDevices2, isSupported2, getHID, TransportWebHID, TransportWebHID_default; -var init_TransportWebHID = __esm({ - "node_modules/@ledgerhq/hw-transport-webhid/lib-es/TransportWebHID.js"() { - init_Transport(); - init_hid_framing(); - init_lib_es3(); - init_lib_es2(); - init_lib_es(); - __awaiter7 = function(thisArg, _arguments, P, generator) { - function adopt(value) { - return value instanceof P ? value : new P(function(resolve) { - resolve(value); - }); +// dist/lib/block.js +function validate(block) { + if (block.account == null) { + throw new Error("Account missing"); + } + if (block.previous == null || block.previous === "") { + throw new Error("Frontier missing"); + } + if (block.representative == null) { + throw new Error("Representative missing"); + } + if (block.balance == null) { + throw new Error("Balance missing"); + } + if (block.balance < 0) { + throw new Error("Negative balance"); + } + switch (block.constructor) { + case SendBlock: { + if (block.link == null || block.link === "") { + throw new Error("Recipient missing"); } - return new (P || (P = Promise))(function(resolve, reject) { - function fulfilled(value) { - try { - step(generator.next(value)); - } catch (e) { - reject(e); - } - } - function rejected(value) { - try { - step(generator["throw"](value)); - } catch (e) { - reject(e); - } + break; + } + case ReceiveBlock: { + if (block.link == null) { + throw new Error("Origin send block hash missing"); + } + break; + } + case ChangeBlock: { + if (block.link == null) { + throw new Error("Change block link missing"); + } + if (+block.link !== 0) { + throw new Error("Invalid change block link"); + } + break; + } + } +} +var Block, SendBlock, ReceiveBlock, ChangeBlock; +var init_block = __esm({ + "dist/lib/block.js"() { + "use strict"; + init_constants(); + init_account(); + init_blake2b(); + init_convert(); + init_nano_nacl(); + init_pool2(); + init_workers(); + Block = class _Block { + static #pool = new Pool2(nano_pow_default); + account; + type = "state"; + constructor(account) { + if (this.constructor === _Block) { + throw new Error("Block is an abstract class and cannot be instantiated directly."); } - function step(result) { - result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); + if (account.constructor === Account) { + this.account = account; + } else if (typeof account === "string") { + this.account = Account.fromAddress(account); + } else { + throw new TypeError("Invalid account"); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); - }; - ledgerDevices2 = [ - { - vendorId: ledgerUSBVendorId } - ]; - isSupported2 = () => Promise.resolve(!!(window.navigator && window.navigator.hid)); - getHID = () => { - const { hid } = navigator; - if (!hid) - throw new TransportError("navigator.hid is not supported", "HIDNotSupported"); - return hid; - }; - TransportWebHID = class _TransportWebHID extends Transport_default { - constructor(device) { - super(); - this.channel = Math.floor(Math.random() * 65535); - this.packetSize = 64; - this.inputs = []; - this.read = () => { - if (this.inputs.length) { - return Promise.resolve(this.inputs.shift()); - } - return new Promise((success) => { - this.inputCallback = success; - }); - }; - this.onInputReport = (e) => { - const buffer2 = Buffer.from(e.data.buffer); - if (this.inputCallback) { - this.inputCallback(buffer2); - this.inputCallback = null; - } else { - this.inputs.push(buffer2); - } - }; - this._disconnectEmitted = false; - this._emitDisconnect = (e) => { - if (this._disconnectEmitted) - return; - this._disconnectEmitted = true; - this.emit("disconnect", e); + /** + * Converts the block to JSON format as expected by the `process` RPC. + * + * @returns {string} JSON representation of the block + */ + json() { + return { + "type": this.type, + "account": this.account.address, + "previous": this.previous, + "representative": this.representative.address, + "balance": this.balance.toString(), + "link": this.link, + "signature": this.signature ?? "", + "work": this.work ?? "" }; - this.exchange = (apdu) => __awaiter7(this, void 0, void 0, function* () { - const b = yield this.exchangeAtomicImpl(() => __awaiter7(this, void 0, void 0, function* () { - const { channel, packetSize } = this; - log("apdu", "=> " + apdu.toString("hex")); - const framing = hid_framing_default(channel, packetSize); - const blocks = framing.makeBlocks(apdu); - for (let i = 0; i < blocks.length; i++) { - yield this.device.sendReport(0, blocks[i]); - } - let result; - let acc; - while (!(result = framing.getReducedResult(acc))) { - const buffer2 = yield this.read(); - acc = framing.reduceResponse(acc, buffer2); - } - log("apdu", "<= " + result.toString("hex")); - return result; - })).catch((e) => { - if (e && e.message && e.message.includes("write")) { - this._emitDisconnect(e); - throw new DisconnectedDeviceDuringOperation(e.message); - } - throw e; - }); - return b; - }); - this.device = device; - this.deviceModel = typeof device.productId === "number" ? identifyUSBProductId(device.productId) : void 0; - device.addEventListener("inputreport", this.onInputReport); } /** - * Similar to create() except it will always display the device permission (even if some devices are already accepted). - */ - static request() { - return __awaiter7(this, void 0, void 0, function* () { - const [device] = yield requestLedgerDevices(); - return _TransportWebHID.open(device); - }); + * Hashes the block using Blake2b. + * + * @returns {Promise} Block data hashed to a byte array + */ + async hash() { + const data = [ + PREAMBLE, + this.account.publicKey, + this.previous, + this.representative.publicKey, + dec.toHex(this.balance, 32), + this.link + ]; + const hash2 = new Blake2b(32); + data.forEach((str) => hash2.update(hex.toBytes(str))); + return hash2.digest("hex").toUpperCase(); + } + /** + * Calculates proof-of-work using a pool of WebGL workers. + * + * A successful response sets the `work` property. + */ + async pow() { + const data = { + "hash": this.previous, + "threshold": this instanceof SendBlock || this instanceof ChangeBlock ? THRESHOLD_SEND : THRESHOLD_RECEIVE + }; + const [{ work }] = await _Block.#pool.assign([data]); + this.work = work; + } + async sign(input, block) { + if (typeof input === "number") { + const index = input; + const { Ledger: Ledger2 } = await Promise.resolve().then(() => (init_ledger(), ledger_exports)); + const ledger = await Ledger2.init(); + await ledger.open(); + if (block) { + try { + await ledger.updateCache(index, block); + } catch (err) { + console.warn("Error updating Ledger cache of previous block, attempting signature anyway", err); + } + } + const result = await ledger.sign(index, this); + if (result.status !== "OK") { + throw new Error(result.status); + } + this.signature = result.signature; + } else { + const key = input ?? this.account.privateKey; + if (key == null) { + throw new Error("No valid key found to sign block"); + } + const account = await Account.fromPrivateKey(key); + try { + const signature = NanoNaCl.detached(hex.toBytes(await this.hash()), hex.toBytes(`${account.privateKey}`)); + this.signature = signature; + } catch (err) { + throw new Error(`Failed to sign block with key ${key}: ${err}`); + } + } } /** - * Similar to create() except it will never display the device permission (it returns a Promise, null if it fails to find a device). - */ - static openConnected() { - return __awaiter7(this, void 0, void 0, function* () { - const devices2 = yield getLedgerDevices2(); - if (devices2.length === 0) - return null; - return _TransportWebHID.open(devices2[0]); - }); + * Sends the block to a node for processing on the network. + * + * The block must already be signed (see `sign()` for more information). + * The block must also have a `work` value. + * + * @param {Rpc|string|URL} rpc - RPC node information required to call `process` + * @returns {Promise} Hash of the processed block + */ + async process(rpc) { + if (!this.signature) { + throw new Error("Block is missing signature. Use sign() and try again."); + } + if (!this.work == null) { + throw new Error("Block is missing proof-of-work. Generate PoW and try again."); + } + const data = { + "subtype": this.subtype, + "json_block": "true", + "block": this.json() + }; + const res = await rpc.call("process", data); + if (res.hash == null) { + throw new Error("Block could not be processed", res); + } + return res.hash; } /** - * Create a Ledger transport with a HIDDevice - */ - static open(device) { - return __awaiter7(this, void 0, void 0, function* () { - yield device.open(); - const transport = new _TransportWebHID(device); - const onDisconnect = (e) => { - if (device === e.device) { - getHID().removeEventListener("disconnect", onDisconnect); - transport._emitDisconnect(new DisconnectedDevice()); - } - }; - getHID().addEventListener("disconnect", onDisconnect); - return transport; - }); + * Verifies the signature of the block. If a key is not provided, the public + * key of the block's account will be used if it exists. + * + * @param {string} [key] - Hexadecimal-formatted public key to use for verification + * @returns {boolean} True if block was signed by the matching private key + */ + async verify(key) { + key ??= this.account.publicKey; + if (!key) { + throw new Error("Provide a key for block signature verification."); + } + const data = [ + PREAMBLE, + this.account.publicKey, + this.previous, + this.representative.publicKey, + dec.toHex(this.balance, 32), + this.link + ]; + const hash2 = new Blake2b(32); + data.forEach((str) => hash2.update(hex.toBytes(str))); + const blockHash = hash2.digest("hex").toUpperCase(); + return NanoNaCl.verify(hex.toBytes(blockHash), hex.toBytes(this.signature ?? ""), hex.toBytes(key)); } - /** - * Release the transport device - */ - close() { - return __awaiter7(this, void 0, void 0, function* () { - yield this.exchangeBusyPromise; - this.device.removeEventListener("inputreport", this.onInputReport); - yield this.device.close(); - }); + }; + SendBlock = class extends Block { + type = "state"; + subtype = "send"; + previous; + representative; + balance; + link; + signature; + work; + constructor(sender, balance, recipient, amount, representative, frontier, work) { + super(sender); + this.previous = frontier; + this.representative = Account.fromAddress(representative); + this.link = Account.fromAddress(recipient).publicKey; + this.work = work ?? ""; + const bigBalance = BigInt(balance); + const bigAmount = BigInt(amount); + this.balance = bigBalance - bigAmount; + validate(this); } - setScrambleKey() { + }; + ReceiveBlock = class extends Block { + type = "state"; + subtype = "receive"; + previous; + representative; + balance; + link; + signature; + work; + constructor(recipient, balance, origin, amount, representative, frontier, work) { + super(recipient); + this.previous = frontier ?? Account.fromAddress(recipient).publicKey; + this.representative = Account.fromAddress(representative); + this.link = origin; + this.work = work ?? ""; + const bigBalance = BigInt(balance); + const bigAmount = BigInt(amount); + this.balance = bigBalance + bigAmount; + validate(this); } }; - TransportWebHID.isSupported = isSupported2; - TransportWebHID.list = getLedgerDevices2; - TransportWebHID.listen = (observer) => { - let unsubscribed = false; - getFirstLedgerDevice2().then((device) => { - if (!device) { - observer.error(new TransportOpenUserCancelled("Access denied to use Ledger device")); - } else if (!unsubscribed) { - const deviceModel = typeof device.productId === "number" ? identifyUSBProductId(device.productId) : void 0; - observer.next({ - type: "add", - descriptor: device, - deviceModel - }); - observer.complete(); - } - }, (error) => { - observer.error(new TransportOpenUserCancelled(error.message)); - }); - function unsubscribe() { - unsubscribed = true; + ChangeBlock = class extends Block { + type = "state"; + subtype = "change"; + previous; + representative; + balance; + link = Account.fromAddress(BURN_ADDRESS).publicKey; + signature; + work; + constructor(account, balance, representative, frontier, work) { + super(account); + this.previous = frontier; + this.representative = Account.fromAddress(representative); + this.balance = BigInt(balance); + this.work = work ?? ""; + validate(this); } - return { - unsubscribe - }; }; - TransportWebHID_default = TransportWebHID; } }); -// dist/lib/ledger.js -var ledger_exports = {}; -__export(ledger_exports, { - Ledger: () => Ledger -}); -var Ledger; -var init_ledger = __esm({ - "dist/lib/ledger.js"() { +// dist/lib/workers/nano-nacl.js +var NanoNaCl2, nano_nacl_default2; +var init_nano_nacl2 = __esm({ + "dist/lib/workers/nano-nacl.js"() { "use strict"; - init_TransportWebBLE(); - init_TransportWebUSB(); - init_TransportWebHID(); - init_constants(); - init_convert(); - init_rpc(); - init_block(); - Ledger = class _Ledger { - static #isInternal = false; - #status = "DISCONNECTED"; - get status() { - return this.#status; + init_blake2b(); + init_pool(); + NanoNaCl2 = class _NanoNaCl extends WorkerInterface { + static { + _NanoNaCl.listen(); } - openTimeout = 3e3; - listenTimeout = 3e4; - transport = null; - DynamicTransport = TransportWebHID_default; - constructor() { - if (!_Ledger.#isInternal) { - throw new Error("Ledger cannot be instantiated directly. Use Ledger.init()"); - } - _Ledger.#isInternal = false; + static async work(data) { + return new Promise(async (resolve, reject) => { + for (let d of data) { + try { + d.publicKey = await this.convert(d.privateKey); + } catch (err) { + reject(err); + } + } + resolve(data); + }); } - static async init() { - _Ledger.#isInternal = true; - const self = new this(); - await self.checkBrowserSupport(); - await self.listen(); - return self; + static gf = function(init) { + const r = new Float64Array(16); + if (init) + for (let i = 0; i < init.length; i++) + r[i] = init[i]; + return r; + }; + static gf0 = this.gf(); + static gf1 = this.gf([1]); + static D = this.gf([30883, 4953, 19914, 30187, 55467, 16705, 2637, 112, 59544, 30585, 16505, 36039, 65139, 11119, 27886, 20995]); + static D2 = this.gf([61785, 9906, 39828, 60374, 45398, 33411, 5274, 224, 53552, 61171, 33010, 6542, 64743, 22239, 55772, 9222]); + static X = this.gf([54554, 36645, 11616, 51542, 42930, 38181, 51040, 26924, 56412, 64982, 57905, 49316, 21502, 52590, 14035, 8553]); + static Y = this.gf([26200, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214]); + static I = this.gf([41136, 18958, 6951, 50414, 58488, 44335, 6150, 12099, 55207, 15867, 153, 11085, 57099, 20417, 9344, 11139]); + static vn(x, xi, y, yi, n) { + let d = 0; + for (let i = 0; i < n; i++) + d |= x[xi + i] ^ y[yi + i]; + return (1 & d - 1 >>> 8) - 1; + } + static crypto_verify_32(x, xi, y, yi) { + return this.vn(x, xi, y, yi, 32); + } + static set25519(r, a) { + for (let i = 0; i < 16; i++) + r[i] = a[i] | 0; } - /** - * Check which transport protocols are supported by the browser and set the - * transport type according to the following priorities: Bluetooth, USB, HID. - */ - async checkBrowserSupport() { - console.log("Checking browser Ledger support..."); - const supports = { - ble: await TransportWebBLE_default.isSupported(), - usb: await TransportWebUSB_default.isSupported(), - hid: await TransportWebHID_default.isSupported() - }; - console.log(`ble: ${supports.ble}; usb: ${supports.usb}; hid: ${supports.hid}`); - if (supports.ble) { - this.DynamicTransport = TransportWebBLE_default; - } else if (supports.usb) { - this.DynamicTransport = TransportWebUSB_default; - } else if (supports.hid) { - this.DynamicTransport = TransportWebHID_default; - } else { - throw new Error("Unsupported browser"); + static car25519(o) { + let v, c = 1; + for (let i = 0; i < 16; i++) { + v = o[i] + c + 65535; + c = Math.floor(v / 65536); + o[i] = v - c * 65536; } + o[0] += c - 1 + 37 * (c - 1); } - async listen() { - const { usb } = globalThis.navigator; - if (usb) { - usb.addEventListener("connect", console.log.bind(console)); - usb.addEventListener("disconnect", console.log.bind(console)); + static sel25519(p, q, b) { + let t; + const c = ~(b - 1); + for (let i = 0; i < 16; i++) { + t = c & (p[i] ^ q[i]); + p[i] ^= t; + q[i] ^= t; } } - async connect() { - const { usb } = globalThis.navigator; - if (usb) { - usb.removeEventListener("disconnect", this.onDisconnectUsb.bind(this)); - usb.addEventListener("disconnect", this.onDisconnectUsb.bind(this)); - } - const version = await this.version(); - if (version.status === "OK") { - if (version.name === "Nano") { - const account = await this.account(); - if (account.status === "OK") { - this.#status = "CONNECTED"; - } else if (account.status === "SECURITY_STATUS_NOT_SATISFIED") { - this.#status = "LOCKED"; - } else { - this.#status = "DISCONNECTED"; - } - } else if (version.name === "BOLOS") { - const open2 = await this.open(); - this.#status = open2.status === "OK" ? "CONNECTED" : "DISCONNECTED"; - } else { - this.#status = "BUSY"; + static pack25519(o, n) { + let b; + const m = this.gf(); + const t = this.gf(); + for (let i = 0; i < 16; i++) + t[i] = n[i]; + this.car25519(t); + this.car25519(t); + this.car25519(t); + for (let j = 0; j < 2; j++) { + m[0] = t[0] - 65517; + for (let i = 1; i < 15; i++) { + m[i] = t[i] - 65535 - (m[i - 1] >> 16 & 1); + m[i - 1] &= 65535; } - } else { - this.#status = "DISCONNECTED"; + m[15] = t[15] - 32767 - (m[14] >> 16 & 1); + b = m[15] >> 16 & 1; + m[14] &= 65535; + this.sel25519(t, m, 1 - b); } - return this.status; - } - async onDisconnectUsb(e) { - if (e.device?.manufacturerName === "Ledger") { - const { usb } = globalThis.navigator; - usb.removeEventListener("disconnect", this.onDisconnectUsb); - this.#status = "DISCONNECTED"; + for (let i = 0; i < 16; i++) { + o[2 * i] = t[i] & 255; + o[2 * i + 1] = t[i] >> 8; } } - /** - * Open Nano app by launching user flow. - * - * https://developers.ledger.com/docs/connectivity/ledgerJS/open-close-info-on-apps#open-application - * - * This command resets the internal USB connection of the device which can - * cause subsequent commands to fail if called too quickly. A one-second delay - * is implemented in this method to mitigate the issue. - * - * https://github.com/LedgerHQ/ledger-live/issues/4964#issuecomment-1878361157 - * - * @returns Status of command - */ - async open() { - const name = new TextEncoder().encode("Nano"); - const transport = await this.DynamicTransport.create(this.openTimeout, this.listenTimeout); - const response = await transport.send(224, 216, 0, 0, name).then((res) => bytes.toDec(res)).catch((err) => err.statusCode); - return new Promise((resolve) => setTimeout(resolve, 1e3, { status: LEDGER_STATUS_CODES[response] })); + static neq25519(a, b) { + const c = new Uint8Array(32); + const d = new Uint8Array(32); + this.pack25519(c, a); + this.pack25519(d, b); + return this.crypto_verify_32(c, 0, d, 0); } - /** - * Close the currently running app. - * - * https://developers.ledger.com/docs/connectivity/ledgerJS/open-close-info-on-apps#quit-application - * - * This command resets the internal USB connection of the device which can - * cause subsequent commands to fail if called too quickly. A one-second delay - * is implemented in this method to mitigate the issue. - * - * https://github.com/LedgerHQ/ledger-live/issues/4964#issuecomment-1878361157 - * - * @returns Status of command - */ - async close() { - const transport = await this.DynamicTransport.create(this.openTimeout, this.listenTimeout); - const response = await transport.send(176, 167, 0, 0).then((res) => bytes.toDec(res)).catch((err) => err.statusCode); - return new Promise((resolve) => setTimeout(resolve, 1e3, { status: LEDGER_STATUS_CODES[response] })); + static par25519(a) { + var d = new Uint8Array(32); + this.pack25519(d, a); + return d[0] & 1; } - /** - * Get the version of the current process. If a specific app is running, get - * the app version. Otherwise, get the Ledger BOLOS version instead. - * - * https://developers.ledger.com/docs/connectivity/ledgerJS/open-close-info-on-apps#get-information - * - * @returns Status, process name, and version - */ - async version() { - const transport = await this.DynamicTransport.create(this.openTimeout, this.listenTimeout); - const response = await transport.send(176, 1, 0, 0).catch((err) => dec.toBytes(err.statusCode)); - await transport.close(); - if (response.length === 2) { - const statusCode2 = bytes.toDec(response); - const status2 = LEDGER_STATUS_CODES[statusCode2] ?? "UNKNOWN_ERROR"; - return { status: status2, name: null, version: null }; - } - const nameLength = response[1]; - const name = response.slice(2, 2 + nameLength).toString(); - const versionLength = response[2 + nameLength]; - const version = response.slice(2 + nameLength + 1, 2 + nameLength + 1 + versionLength).toString(); - const statusCode = bytes.toDec(response.slice(-2)); - const status = LEDGER_STATUS_CODES[statusCode]; - return { status, name, version }; + static unpack25519(o, n) { + for (let i = 0; i < 16; i++) + o[i] = n[2 * i] + (n[2 * i + 1] << 8); + o[15] &= 32767; + } + static A(o, a, b) { + for (let i = 0; i < 16; i++) + o[i] = a[i] + b[i]; + } + static Z(o, a, b) { + for (let i = 0; i < 16; i++) + o[i] = a[i] - b[i]; + } + static M(o, a, b) { + let v, c, t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0, t8 = 0, t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0, t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0, t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0, b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5], b6 = b[6], b7 = b[7], b8 = b[8], b9 = b[9], b10 = b[10], b11 = b[11], b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15]; + v = a[0]; + t0 += v * b0; + t1 += v * b1; + t2 += v * b2; + t3 += v * b3; + t4 += v * b4; + t5 += v * b5; + t6 += v * b6; + t7 += v * b7; + t8 += v * b8; + t9 += v * b9; + t10 += v * b10; + t11 += v * b11; + t12 += v * b12; + t13 += v * b13; + t14 += v * b14; + t15 += v * b15; + v = a[1]; + t1 += v * b0; + t2 += v * b1; + t3 += v * b2; + t4 += v * b3; + t5 += v * b4; + t6 += v * b5; + t7 += v * b6; + t8 += v * b7; + t9 += v * b8; + t10 += v * b9; + t11 += v * b10; + t12 += v * b11; + t13 += v * b12; + t14 += v * b13; + t15 += v * b14; + t16 += v * b15; + v = a[2]; + t2 += v * b0; + t3 += v * b1; + t4 += v * b2; + t5 += v * b3; + t6 += v * b4; + t7 += v * b5; + t8 += v * b6; + t9 += v * b7; + t10 += v * b8; + t11 += v * b9; + t12 += v * b10; + t13 += v * b11; + t14 += v * b12; + t15 += v * b13; + t16 += v * b14; + t17 += v * b15; + v = a[3]; + t3 += v * b0; + t4 += v * b1; + t5 += v * b2; + t6 += v * b3; + t7 += v * b4; + t8 += v * b5; + t9 += v * b6; + t10 += v * b7; + t11 += v * b8; + t12 += v * b9; + t13 += v * b10; + t14 += v * b11; + t15 += v * b12; + t16 += v * b13; + t17 += v * b14; + t18 += v * b15; + v = a[4]; + t4 += v * b0; + t5 += v * b1; + t6 += v * b2; + t7 += v * b3; + t8 += v * b4; + t9 += v * b5; + t10 += v * b6; + t11 += v * b7; + t12 += v * b8; + t13 += v * b9; + t14 += v * b10; + t15 += v * b11; + t16 += v * b12; + t17 += v * b13; + t18 += v * b14; + t19 += v * b15; + v = a[5]; + t5 += v * b0; + t6 += v * b1; + t7 += v * b2; + t8 += v * b3; + t9 += v * b4; + t10 += v * b5; + t11 += v * b6; + t12 += v * b7; + t13 += v * b8; + t14 += v * b9; + t15 += v * b10; + t16 += v * b11; + t17 += v * b12; + t18 += v * b13; + t19 += v * b14; + t20 += v * b15; + v = a[6]; + t6 += v * b0; + t7 += v * b1; + t8 += v * b2; + t9 += v * b3; + t10 += v * b4; + t11 += v * b5; + t12 += v * b6; + t13 += v * b7; + t14 += v * b8; + t15 += v * b9; + t16 += v * b10; + t17 += v * b11; + t18 += v * b12; + t19 += v * b13; + t20 += v * b14; + t21 += v * b15; + v = a[7]; + t7 += v * b0; + t8 += v * b1; + t9 += v * b2; + t10 += v * b3; + t11 += v * b4; + t12 += v * b5; + t13 += v * b6; + t14 += v * b7; + t15 += v * b8; + t16 += v * b9; + t17 += v * b10; + t18 += v * b11; + t19 += v * b12; + t20 += v * b13; + t21 += v * b14; + t22 += v * b15; + v = a[8]; + t8 += v * b0; + t9 += v * b1; + t10 += v * b2; + t11 += v * b3; + t12 += v * b4; + t13 += v * b5; + t14 += v * b6; + t15 += v * b7; + t16 += v * b8; + t17 += v * b9; + t18 += v * b10; + t19 += v * b11; + t20 += v * b12; + t21 += v * b13; + t22 += v * b14; + t23 += v * b15; + v = a[9]; + t9 += v * b0; + t10 += v * b1; + t11 += v * b2; + t12 += v * b3; + t13 += v * b4; + t14 += v * b5; + t15 += v * b6; + t16 += v * b7; + t17 += v * b8; + t18 += v * b9; + t19 += v * b10; + t20 += v * b11; + t21 += v * b12; + t22 += v * b13; + t23 += v * b14; + t24 += v * b15; + v = a[10]; + t10 += v * b0; + t11 += v * b1; + t12 += v * b2; + t13 += v * b3; + t14 += v * b4; + t15 += v * b5; + t16 += v * b6; + t17 += v * b7; + t18 += v * b8; + t19 += v * b9; + t20 += v * b10; + t21 += v * b11; + t22 += v * b12; + t23 += v * b13; + t24 += v * b14; + t25 += v * b15; + v = a[11]; + t11 += v * b0; + t12 += v * b1; + t13 += v * b2; + t14 += v * b3; + t15 += v * b4; + t16 += v * b5; + t17 += v * b6; + t18 += v * b7; + t19 += v * b8; + t20 += v * b9; + t21 += v * b10; + t22 += v * b11; + t23 += v * b12; + t24 += v * b13; + t25 += v * b14; + t26 += v * b15; + v = a[12]; + t12 += v * b0; + t13 += v * b1; + t14 += v * b2; + t15 += v * b3; + t16 += v * b4; + t17 += v * b5; + t18 += v * b6; + t19 += v * b7; + t20 += v * b8; + t21 += v * b9; + t22 += v * b10; + t23 += v * b11; + t24 += v * b12; + t25 += v * b13; + t26 += v * b14; + t27 += v * b15; + v = a[13]; + t13 += v * b0; + t14 += v * b1; + t15 += v * b2; + t16 += v * b3; + t17 += v * b4; + t18 += v * b5; + t19 += v * b6; + t20 += v * b7; + t21 += v * b8; + t22 += v * b9; + t23 += v * b10; + t24 += v * b11; + t25 += v * b12; + t26 += v * b13; + t27 += v * b14; + t28 += v * b15; + v = a[14]; + t14 += v * b0; + t15 += v * b1; + t16 += v * b2; + t17 += v * b3; + t18 += v * b4; + t19 += v * b5; + t20 += v * b6; + t21 += v * b7; + t22 += v * b8; + t23 += v * b9; + t24 += v * b10; + t25 += v * b11; + t26 += v * b12; + t27 += v * b13; + t28 += v * b14; + t29 += v * b15; + v = a[15]; + t15 += v * b0; + t16 += v * b1; + t17 += v * b2; + t18 += v * b3; + t19 += v * b4; + t20 += v * b5; + t21 += v * b6; + t22 += v * b7; + t23 += v * b8; + t24 += v * b9; + t25 += v * b10; + t26 += v * b11; + t27 += v * b12; + t28 += v * b13; + t29 += v * b14; + t30 += v * b15; + t0 += 38 * t16; + t1 += 38 * t17; + t2 += 38 * t18; + t3 += 38 * t19; + t4 += 38 * t20; + t5 += 38 * t21; + t6 += 38 * t22; + t7 += 38 * t23; + t8 += 38 * t24; + t9 += 38 * t25; + t10 += 38 * t26; + t11 += 38 * t27; + t12 += 38 * t28; + t13 += 38 * t29; + t14 += 38 * t30; + c = 1; + v = t0 + c + 65535; + c = Math.floor(v / 65536); + t0 = v - c * 65536; + v = t1 + c + 65535; + c = Math.floor(v / 65536); + t1 = v - c * 65536; + v = t2 + c + 65535; + c = Math.floor(v / 65536); + t2 = v - c * 65536; + v = t3 + c + 65535; + c = Math.floor(v / 65536); + t3 = v - c * 65536; + v = t4 + c + 65535; + c = Math.floor(v / 65536); + t4 = v - c * 65536; + v = t5 + c + 65535; + c = Math.floor(v / 65536); + t5 = v - c * 65536; + v = t6 + c + 65535; + c = Math.floor(v / 65536); + t6 = v - c * 65536; + v = t7 + c + 65535; + c = Math.floor(v / 65536); + t7 = v - c * 65536; + v = t8 + c + 65535; + c = Math.floor(v / 65536); + t8 = v - c * 65536; + v = t9 + c + 65535; + c = Math.floor(v / 65536); + t9 = v - c * 65536; + v = t10 + c + 65535; + c = Math.floor(v / 65536); + t10 = v - c * 65536; + v = t11 + c + 65535; + c = Math.floor(v / 65536); + t11 = v - c * 65536; + v = t12 + c + 65535; + c = Math.floor(v / 65536); + t12 = v - c * 65536; + v = t13 + c + 65535; + c = Math.floor(v / 65536); + t13 = v - c * 65536; + v = t14 + c + 65535; + c = Math.floor(v / 65536); + t14 = v - c * 65536; + v = t15 + c + 65535; + c = Math.floor(v / 65536); + t15 = v - c * 65536; + t0 += c - 1 + 37 * (c - 1); + c = 1; + v = t0 + c + 65535; + c = Math.floor(v / 65536); + t0 = v - c * 65536; + v = t1 + c + 65535; + c = Math.floor(v / 65536); + t1 = v - c * 65536; + v = t2 + c + 65535; + c = Math.floor(v / 65536); + t2 = v - c * 65536; + v = t3 + c + 65535; + c = Math.floor(v / 65536); + t3 = v - c * 65536; + v = t4 + c + 65535; + c = Math.floor(v / 65536); + t4 = v - c * 65536; + v = t5 + c + 65535; + c = Math.floor(v / 65536); + t5 = v - c * 65536; + v = t6 + c + 65535; + c = Math.floor(v / 65536); + t6 = v - c * 65536; + v = t7 + c + 65535; + c = Math.floor(v / 65536); + t7 = v - c * 65536; + v = t8 + c + 65535; + c = Math.floor(v / 65536); + t8 = v - c * 65536; + v = t9 + c + 65535; + c = Math.floor(v / 65536); + t9 = v - c * 65536; + v = t10 + c + 65535; + c = Math.floor(v / 65536); + t10 = v - c * 65536; + v = t11 + c + 65535; + c = Math.floor(v / 65536); + t11 = v - c * 65536; + v = t12 + c + 65535; + c = Math.floor(v / 65536); + t12 = v - c * 65536; + v = t13 + c + 65535; + c = Math.floor(v / 65536); + t13 = v - c * 65536; + v = t14 + c + 65535; + c = Math.floor(v / 65536); + t14 = v - c * 65536; + v = t15 + c + 65535; + c = Math.floor(v / 65536); + t15 = v - c * 65536; + t0 += c - 1 + 37 * (c - 1); + o[0] = t0; + o[1] = t1; + o[2] = t2; + o[3] = t3; + o[4] = t4; + o[5] = t5; + o[6] = t6; + o[7] = t7; + o[8] = t8; + o[9] = t9; + o[10] = t10; + o[11] = t11; + o[12] = t12; + o[13] = t13; + o[14] = t14; + o[15] = t15; } - /** - * Get an account at a specific BIP-44 index. - * - * @returns Response object containing command status, public key, and address - */ - async account(index = 0, show = false) { - if (typeof index !== "number" || index < 0 || index >= HARDENED_OFFSET) { - throw new TypeError("Invalid account index"); - } - const purpose = dec.toBytes(BIP44_PURPOSE + HARDENED_OFFSET, 4); - const coin = dec.toBytes(BIP44_COIN_NANO + HARDENED_OFFSET, 4); - const account = dec.toBytes(index + HARDENED_OFFSET, 4); - const data = new Uint8Array([3, ...purpose, ...coin, ...account]); - const transport = await this.DynamicTransport.create(this.openTimeout, this.listenTimeout); - const response = await transport.send(161, 2, show ? 1 : 0, 0, data).catch((err) => dec.toBytes(err.statusCode)); - await transport.close(); - if (response.length === 2) { - const statusCode = bytes.toDec(response); - const status = LEDGER_STATUS_CODES[statusCode] ?? "UNKNOWN_ERROR"; - return { status, publicKey: null, address: null }; - } - try { - const publicKey = bytes.toHex(response.slice(0, 32)); - const addressLength = response[32]; - const address = response.slice(33, 33 + addressLength).toString(); - const statusCode = bytes.toDec(response.slice(33 + addressLength)); - const status = LEDGER_STATUS_CODES[statusCode]; - return { status, publicKey, address }; - } catch (err) { - return { status: "ERROR_PARSING_ACCOUNT", publicKey: null, address: null }; - } + static S(o, a) { + this.M(o, a, a); } - /** - * Cache frontier block in device memory. - * - * @param {number} index - Account number - * @param {any} block - Block data to cache - * @returns Status of command - */ - async cacheBlock(index = 0, block) { - if (typeof index !== "number" || index < 0 || index >= HARDENED_OFFSET) { - throw new TypeError("Invalid account index"); - } - if (!(block instanceof SendBlock) && !(block instanceof ReceiveBlock) && !(block instanceof ChangeBlock)) { - throw new TypeError("Invalid block format"); - } - if (!block.signature) { - throw new ReferenceError("Cannot cache unsigned block"); + static inv25519(o, i) { + const c = this.gf(); + for (let a = 0; a < 16; a++) + c[a] = i[a]; + for (let a = 253; a >= 0; a--) { + this.S(c, c); + if (a !== 2 && a !== 4) + this.M(c, c, i); } - const purpose = dec.toBytes(BIP44_PURPOSE + HARDENED_OFFSET, 4); - const coin = dec.toBytes(BIP44_COIN_NANO + HARDENED_OFFSET, 4); - const account = dec.toBytes(index + HARDENED_OFFSET, 4); - const previous = hex.toBytes(block.previous); - const link = hex.toBytes(block.link); - const representative = hex.toBytes(block.representative.publicKey); - const balance = hex.toBytes(BigInt(block.balance).toString(16), 16); - const signature = hex.toBytes(block.signature); - const data = new Uint8Array([3, ...purpose, ...coin, ...account, ...previous, ...link, ...representative, ...balance, ...signature]); - const transport = await this.DynamicTransport.create(this.openTimeout, this.listenTimeout); - const response = await transport.send(161, 3, 0, 0, data).then((res) => bytes.toDec(res)).catch((err) => err.statusCode); - await transport.close(); - return { status: LEDGER_STATUS_CODES[response] }; + for (let a = 0; a < 16; a++) + o[a] = c[a]; } - async sign(index = 0, input) { - if (typeof index !== "number" || index < 0 || index >= HARDENED_OFFSET) { - throw new TypeError("Invalid account index"); - } - const purpose = dec.toBytes(BIP44_PURPOSE + HARDENED_OFFSET, 4); - const coin = dec.toBytes(BIP44_COIN_NANO + HARDENED_OFFSET, 4); - const account = dec.toBytes(index + HARDENED_OFFSET, 4); - const transport = await this.DynamicTransport.create(this.openTimeout, this.listenTimeout); - if (typeof input === "string") { - const nonce = utf8.toBytes(input); - if (nonce.length !== 16) { - throw new RangeError("Nonce must be 16-byte string"); - } - const data = new Uint8Array([3, ...purpose, ...coin, ...account, ...nonce]); - const response = await transport.send(161, 5, 0, 0, data).catch((err) => dec.toBytes(err.statusCode)); - await transport.close(); - if (response.length === 2) { - const statusCode2 = bytes.toDec(response); - const status2 = LEDGER_STATUS_CODES[statusCode2] ?? "UNKNOWN_ERROR"; - return { status: status2, signature: null }; - } - const signature = bytes.toHex(response.slice(0, 64)); - const statusCode = bytes.toDec(response.slice(-2)); - const status = LEDGER_STATUS_CODES[statusCode]; - return { status, signature }; - } else { - const previous = hex.toBytes(input.previous); - const link = hex.toBytes(input.link); - const representative = hex.toBytes(input.representative.publicKey); - const balance = hex.toBytes(BigInt(input.balance).toString(16), 16); - const data = new Uint8Array([3, ...purpose, ...coin, ...account, ...previous, ...link, ...representative, ...balance]); - const response = await transport.send(161, 4, 0, 0, data).catch((err) => dec.toBytes(err.statusCode)); - await transport.close(); - if (response.length === 2) { - const statusCode2 = bytes.toDec(response); - const status2 = LEDGER_STATUS_CODES[statusCode2] ?? "UNKNOWN_ERROR"; - return { status: status2, signature: null, hash: null }; - } - const hash2 = bytes.toHex(response.slice(0, 32)); - const signature = bytes.toHex(response.slice(32, 96)); - const statusCode = bytes.toDec(response.slice(-2)); - const status = LEDGER_STATUS_CODES[statusCode]; - return { status, signature, hash: hash2 }; + static pow2523(o, i) { + const c = this.gf(); + for (let a = 0; a < 16; a++) + c[a] = i[a]; + for (let a = 250; a >= 0; a--) { + this.S(c, c); + if (a !== 1) + this.M(c, c, i); } + for (let a = 0; a < 16; a++) + o[a] = c[a]; } - async updateCache(index, input, node) { - if (typeof input === "string" && node?.constructor === Rpc) { - const data = { - "json_block": "true", - "hash": input - }; - const res = await node.call("block_info", data); - if (!res || res.ok === false) { - throw new Error(`Unable to fetch block info`, res); - } - input = res.contents; - } - return this.cacheBlock(index, input); + // Note: difference from TweetNaCl - BLAKE2b used to hash instead of SHA-512. + static crypto_hash(out, m, n) { + const input = new Uint8Array(n); + for (let i = 0; i < n; ++i) + input[i] = m[i]; + const hash2 = new Blake2b(64).update(m).digest(); + for (let i = 0; i < 64; ++i) + out[i] = hash2[i]; + return 0; } - }; - } -}); - -// dist/lib/block.js -function validate(block) { - if (block.account == null) { - throw new Error("Account missing"); - } - if (block.previous == null || block.previous === "") { - throw new Error("Frontier missing"); - } - if (block.representative == null) { - throw new Error("Representative missing"); - } - if (block.balance == null) { - throw new Error("Balance missing"); - } - if (block.balance < 0) { - throw new Error("Negative balance"); - } - switch (block.constructor) { - case SendBlock: { - if (block.link == null || block.link === "") { - throw new Error("Recipient missing"); + static add(p, q) { + const a = this.gf(); + const b = this.gf(); + const c = this.gf(); + const d = this.gf(); + const e = this.gf(); + const f = this.gf(); + const g = this.gf(); + const h = this.gf(); + const t = this.gf(); + this.Z(a, p[1], p[0]); + this.Z(t, q[1], q[0]); + this.M(a, a, t); + this.A(b, p[0], p[1]); + this.A(t, q[0], q[1]); + this.M(b, b, t); + this.M(c, p[3], q[3]); + this.M(c, c, this.D2); + this.M(d, p[2], q[2]); + this.A(d, d, d); + this.Z(e, b, a); + this.Z(f, d, c); + this.A(g, d, c); + this.A(h, b, a); + this.M(p[0], e, f); + this.M(p[1], h, g); + this.M(p[2], g, f); + this.M(p[3], e, h); } - break; - } - case ReceiveBlock: { - if (block.link == null) { - throw new Error("Origin send block hash missing"); + static cswap(p, q, b) { + for (let i = 0; i < 4; i++) { + this.sel25519(p[i], q[i], b); + } } - break; - } - case ChangeBlock: { - if (block.link == null) { - throw new Error("Change block link missing"); + static pack(r, p) { + const tx = this.gf(); + const ty = this.gf(); + const zi = this.gf(); + this.inv25519(zi, p[2]); + this.M(tx, p[0], zi); + this.M(ty, p[1], zi); + this.pack25519(r, ty); + r[31] ^= this.par25519(tx) << 7; } - if (+block.link !== 0) { - throw new Error("Invalid change block link"); + static scalarmult(p, q, s) { + this.set25519(p[0], this.gf0); + this.set25519(p[1], this.gf1); + this.set25519(p[2], this.gf1); + this.set25519(p[3], this.gf0); + for (let i = 255; i >= 0; --i) { + const b = s[i / 8 | 0] >> (i & 7) & 1; + this.cswap(p, q, b); + this.add(q, p); + this.add(p, p); + this.cswap(p, q, b); + } } - break; - } - } -} -var Block, SendBlock, ReceiveBlock, ChangeBlock; -var init_block = __esm({ - "dist/lib/block.js"() { - "use strict"; - init_constants(); - init_account(); - init_blake2b(); - init_convert(); - init_nano_nacl(); - init_pool(); - init_workers(); - Block = class _Block { - static #pool = new Pool(powgpu_default); - account; - type = "state"; - constructor(account) { - if (this.constructor === _Block) { - throw new Error("Block is an abstract class and cannot be instantiated directly."); + static scalarbase(p, s) { + const q = [this.gf(), this.gf(), this.gf(), this.gf()]; + this.set25519(q[0], this.X); + this.set25519(q[1], this.Y); + this.set25519(q[2], this.gf1); + this.M(q[3], this.X, this.Y); + this.scalarmult(p, q, s); + } + static L = new Float64Array([ + 237, + 211, + 245, + 92, + 26, + 99, + 18, + 88, + 214, + 156, + 247, + 162, + 222, + 249, + 222, + 20, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16 + ]); + static modL(r, x) { + let carry, i, j, k; + for (i = 63; i >= 32; --i) { + carry = 0; + for (j = i - 32, k = i - 12; j < k; ++j) { + x[j] += carry - 16 * x[i] * this.L[j - (i - 32)]; + carry = Math.floor((x[j] + 128) / 256); + x[j] -= carry * 256; + } + x[j] += carry; + x[i] = 0; } - if (account.constructor === Account) { - this.account = account; - } else if (typeof account === "string") { - this.account = Account.fromAddress(account); - } else { - throw new TypeError("Invalid account"); + carry = 0; + for (j = 0; j < 32; j++) { + x[j] += carry - (x[31] >> 4) * this.L[j]; + carry = x[j] >> 8; + x[j] &= 255; + } + for (j = 0; j < 32; j++) + x[j] -= carry * this.L[j]; + for (i = 0; i < 32; i++) { + x[i + 1] += x[i] >> 8; + r[i] = x[i] & 255; } } - /** - * Converts the block to JSON format as expected by the `process` RPC. - * - * @returns {string} JSON representation of the block - */ - json() { - return { - "type": this.type, - "account": this.account.address, - "previous": this.previous, - "representative": this.representative.address, - "balance": this.balance.toString(), - "link": this.link, - "signature": this.signature ?? "", - "work": this.work ?? "" - }; + static reduce(r) { + let x = new Float64Array(64); + for (let i = 0; i < 64; i++) + x[i] = r[i]; + for (let i = 0; i < 64; i++) + r[i] = 0; + this.modL(r, x); } - /** - * Hashes the block using Blake2b. - * - * @returns {Promise} Block data hashed to a byte array - */ - async hash() { - const data = [ - PREAMBLE, - this.account.publicKey, - this.previous, - this.representative.publicKey, - dec.toHex(this.balance, 32), - this.link - ]; - const hash2 = new Blake2b(32); - data.forEach((str) => hash2.update(hex.toBytes(str))); - return hash2.digest("hex").toUpperCase(); + // Note: difference from C - smlen returned, not passed as argument. + static crypto_sign(sm, m, n, sk, pk) { + const d = new Uint8Array(64); + const h = new Uint8Array(64); + const r = new Uint8Array(64); + const x = new Float64Array(64); + const p = [this.gf(), this.gf(), this.gf(), this.gf()]; + this.crypto_hash(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + const smlen = n + 64; + for (let i = 0; i < n; i++) + sm[64 + i] = m[i]; + for (let i = 0; i < 32; i++) + sm[32 + i] = d[32 + i]; + this.crypto_hash(r, sm.subarray(32), n + 32); + this.reduce(r); + this.scalarbase(p, r); + this.pack(sm, p); + for (let i = 0; i < 32; i++) + sm[i + 32] = pk[i]; + this.crypto_hash(h, sm, n + 64); + this.reduce(h); + for (let i = 0; i < 64; i++) + x[i] = 0; + for (let i = 0; i < 32; i++) + x[i] = r[i]; + for (let i = 0; i < 32; i++) { + for (let j = 0; j < 32; j++) { + x[i + j] += h[i] * d[j]; + } + } + this.modL(sm.subarray(32), x); + return smlen; } - /** - * Calculates proof-of-work using a pool of WebGL workers. - * - * A successful response sets the `work` property. - */ - async pow() { - const data = { - "hash": this.previous, - "threshold": this instanceof SendBlock || this instanceof ChangeBlock ? THRESHOLD_SEND : THRESHOLD_RECEIVE - }; - const [{ work }] = await _Block.#pool.assign([data]); - this.work = work; + static unpackneg(r, p) { + const t = this.gf(); + const chk = this.gf(); + const num = this.gf(); + const den = this.gf(); + const den2 = this.gf(); + const den4 = this.gf(); + const den6 = this.gf(); + this.set25519(r[2], this.gf1); + this.unpack25519(r[1], p); + this.S(num, r[1]); + this.M(den, num, this.D); + this.Z(num, num, r[2]); + this.A(den, r[2], den); + this.S(den2, den); + this.S(den4, den2); + this.M(den6, den4, den2); + this.M(t, den6, num); + this.M(t, t, den); + this.pow2523(t, t); + this.M(t, t, num); + this.M(t, t, den); + this.M(t, t, den); + this.M(r[0], t, den); + this.S(chk, r[0]); + this.M(chk, chk, den); + if (this.neq25519(chk, num)) + this.M(r[0], r[0], this.I); + this.S(chk, r[0]); + this.M(chk, chk, den); + if (this.neq25519(chk, num)) + return -1; + if (this.par25519(r[0]) === p[31] >> 7) + this.Z(r[0], this.gf0, r[0]); + this.M(r[3], r[0], r[1]); + return 0; } - async sign(input, block) { - if (typeof input === "number") { - const index = input; - const { Ledger: Ledger2 } = await Promise.resolve().then(() => (init_ledger(), ledger_exports)); - const ledger = await Ledger2.init(); - await ledger.open(); - if (block) { - try { - await ledger.updateCache(index, block); - } catch (err) { - console.warn("Error updating Ledger cache of previous block, attempting signature anyway", err); - } - } - const result = await ledger.sign(index, this); - if (result.status !== "OK") { - throw new Error(result.status); - } - this.signature = result.signature; - } else { - const key = input ?? this.account.privateKey; - if (key == null) { - throw new Error("No valid key found to sign block"); - } - const account = await Account.fromPrivateKey(key); - try { - const signature = NanoNaCl.detached(hex.toBytes(await this.hash()), hex.toBytes(`${account.privateKey}`)); - this.signature = signature; - } catch (err) { - throw new Error(`Failed to sign block with key ${key}: ${err}`); - } + static crypto_sign_open(m, sm, n, pk) { + const t = new Uint8Array(32); + const h = new Uint8Array(64); + const p = [this.gf(), this.gf(), this.gf(), this.gf()]; + const q = [this.gf(), this.gf(), this.gf(), this.gf()]; + if (n < 64) + return -1; + if (this.unpackneg(q, pk)) + return -1; + for (let i = 0; i < n; i++) + m[i] = sm[i]; + for (let i = 0; i < 32; i++) + m[i + 32] = pk[i]; + this.crypto_hash(h, m, n); + this.reduce(h); + this.scalarmult(p, q, h); + this.scalarbase(q, sm.subarray(32)); + this.add(p, q); + this.pack(t, p); + n -= 64; + if (this.crypto_verify_32(sm, 0, t, 0)) { + for (let i = 0; i < n; i++) + m[i] = 0; + return -1; } + for (let i = 0; i < n; i++) + m[i] = sm[i + 64]; + return n; } - /** - * Sends the block to a node for processing on the network. - * - * The block must already be signed (see `sign()` for more information). - * The block must also have a `work` value. - * - * @param {Rpc|string|URL} rpc - RPC node information required to call `process` - * @returns {Promise} Hash of the processed block - */ - async process(rpc) { - if (!this.signature) { - throw new Error("Block is missing signature. Use sign() and try again."); - } - if (!this.work == null) { - throw new Error("Block is missing proof-of-work. Generate PoW and try again."); - } - const data = { - "subtype": this.subtype, - "json_block": "true", - "block": this.json() - }; - const res = await rpc.call("process", data); - if (res.hash == null) { - throw new Error("Block could not be processed", res); + static crypto_sign_BYTES = 64; + static crypto_sign_PUBLICKEYBYTES = 32; + static crypto_sign_SECRETKEYBYTES = 32; + static crypto_sign_SEEDBYTES = 32; + /* High-level API */ + static checkArrayTypes(...args) { + for (let i = 0; i < args.length; i++) { + if (!(args[i] instanceof Uint8Array)) + throw new TypeError(`expected Uint8Array; received ${args[i].constructor?.name ?? typeof args[i]}`); } - return res.hash; } - /** - * Verifies the signature of the block. If a key is not provided, the public - * key of the block's account will be used if it exists. - * - * @param {string} [key] - Hexadecimal-formatted public key to use for verification - * @returns {boolean} True if block was signed by the matching private key - */ - async verify(key) { - key ??= this.account.publicKey; - if (!key) { - throw new Error("Provide a key for block signature verification."); + static parseHex(hex2) { + if (hex2.length % 2 === 1) + hex2 = `0${hex2}`; + const arr = hex2.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16)); + return Uint8Array.from(arr ?? []); + } + static hexify(buf) { + let str = ""; + for (let i = 0; i < buf.length; i++) { + if (typeof buf[i] !== "number") + throw new TypeError(`expected number to convert to hex; received ${typeof buf[i]}`); + if (buf[i] < 0 || buf[i] > 255) + throw new RangeError(`expected byte value 0-255; received ${buf[i]}`); + str += buf[i].toString(16).padStart(2, "0"); } - const data = [ - PREAMBLE, - this.account.publicKey, - this.previous, - this.representative.publicKey, - dec.toHex(this.balance, 32), - this.link - ]; - const hash2 = new Blake2b(32); - data.forEach((str) => hash2.update(hex.toBytes(str))); - const blockHash = hash2.digest("hex").toUpperCase(); - return NanoNaCl.verify(hex.toBytes(blockHash), hex.toBytes(this.signature ?? ""), hex.toBytes(key)); + return str; } - }; - SendBlock = class extends Block { - type = "state"; - subtype = "send"; - previous; - representative; - balance; - link; - signature; - work; - constructor(sender, balance, recipient, amount, representative, frontier, work) { - super(sender); - this.previous = frontier; - this.representative = Account.fromAddress(representative); - this.link = Account.fromAddress(recipient).publicKey; - this.work = work ?? ""; - const bigBalance = BigInt(balance); - const bigAmount = BigInt(amount); - this.balance = bigBalance - bigAmount; - validate(this); + static sign(msg, secretKey) { + this.checkArrayTypes(msg, secretKey); + if (secretKey.length !== this.crypto_sign_SECRETKEYBYTES) + throw new Error("bad secret key size"); + var signedMsg = new Uint8Array(this.crypto_sign_BYTES + msg.length); + const publicKey = this.parseHex(this.convert(secretKey)); + this.crypto_sign(signedMsg, msg, msg.length, secretKey, publicKey); + return signedMsg; } - }; - ReceiveBlock = class extends Block { - type = "state"; - subtype = "receive"; - previous; - representative; - balance; - link; - signature; - work; - constructor(recipient, balance, origin, amount, representative, frontier, work) { - super(recipient); - this.previous = frontier ?? Account.fromAddress(recipient).publicKey; - this.representative = Account.fromAddress(representative); - this.link = origin; - this.work = work ?? ""; - const bigBalance = BigInt(balance); - const bigAmount = BigInt(amount); - this.balance = bigBalance + bigAmount; - validate(this); + static open(signedMsg, publicKey) { + this.checkArrayTypes(signedMsg, publicKey); + if (publicKey.length !== this.crypto_sign_PUBLICKEYBYTES) + throw new Error("bad public key size"); + const tmp = new Uint8Array(signedMsg.length); + var mlen = this.crypto_sign_open(tmp, signedMsg, signedMsg.length, publicKey); + if (mlen < 0) + return new Uint8Array(0); + var m = new Uint8Array(mlen); + for (var i = 0; i < m.length; i++) + m[i] = tmp[i]; + return m; } - }; - ChangeBlock = class extends Block { - type = "state"; - subtype = "change"; - previous; - representative; - balance; - link = Account.fromAddress(BURN_ADDRESS).publicKey; - signature; - work; - constructor(account, balance, representative, frontier, work) { - super(account); - this.previous = frontier; - this.representative = Account.fromAddress(representative); - this.balance = BigInt(balance); - this.work = work ?? ""; - validate(this); + static detached(msg, secretKey) { + var signedMsg = this.sign(msg, secretKey); + var sig = new Uint8Array(this.crypto_sign_BYTES); + for (var i = 0; i < sig.length; i++) + sig[i] = signedMsg[i]; + return this.hexify(sig).toUpperCase(); + } + static verify(msg, sig, publicKey) { + this.checkArrayTypes(msg, sig, publicKey); + if (sig.length !== this.crypto_sign_BYTES) + throw new Error("bad signature size"); + if (publicKey.length !== this.crypto_sign_PUBLICKEYBYTES) + throw new Error("bad public key size"); + const sm = new Uint8Array(this.crypto_sign_BYTES + msg.length); + const m = new Uint8Array(this.crypto_sign_BYTES + msg.length); + for (let i = 0; i < this.crypto_sign_BYTES; i++) + sm[i] = sig[i]; + for (let i = 0; i < msg.length; i++) + sm[i + this.crypto_sign_BYTES] = msg[i]; + return this.crypto_sign_open(m, sm, sm.length, publicKey) >= 0; + } + static convert(seed) { + if (typeof seed === "string") + seed = this.parseHex(seed); + this.checkArrayTypes(seed); + if (seed.length !== this.crypto_sign_SEEDBYTES) + throw new Error("bad seed size"); + const pk = new Uint8Array(this.crypto_sign_PUBLICKEYBYTES); + const p = [this.gf(), this.gf(), this.gf(), this.gf()]; + const hash2 = new Blake2b(64).update(seed).digest(); + hash2[0] &= 248; + hash2[31] &= 127; + hash2[31] |= 64; + this.scalarbase(p, hash2); + this.pack(pk, p); + return this.hexify(pk).toUpperCase(); } }; + nano_nacl_default2 = ` + const Blake2b = ${Blake2b} + const WorkerInterface = ${WorkerInterface} + const NanoNaCl = ${NanoNaCl2} +`; } }); @@ -10617,7 +12876,7 @@ async function convert(amount, inputUnit, outputUnit) { async function sign(key, ...input) { const account = await Account.fromPrivateKey(key); const data = hash(input); - const signature = NanoNaCl.detached(hex.toBytes(data), hex.toBytes(`${account.privateKey}`)); + const signature = NanoNaCl2.detached(hex.toBytes(data), hex.toBytes(`${account.privateKey}`)); return signature; } async function sweep(rpc, wallet, recipient, from2 = 0, to = from2) { @@ -10658,7 +12917,7 @@ async function sweep(rpc, wallet, recipient, from2 = 0, to = from2) { async function verify(key, signature, ...input) { const data = hash(input); try { - return await NanoNaCl.verify(hex.toBytes(data), hex.toBytes(signature), hex.toBytes(key)); + return await NanoNaCl2.verify(hex.toBytes(data), hex.toBytes(signature), hex.toBytes(key)); } catch (err) { console.error(err); return false; @@ -10674,7 +12933,7 @@ var init_tools = __esm({ init_convert(); init_rpc(); init_block(); - init_nano_nacl(); + init_nano_nacl2(); Tools = { convert, sign, sweep, verify }; } }); @@ -10688,8 +12947,8 @@ __export(main_exports, { Blake2bWallet: () => Blake2bWallet, ChangeBlock: () => ChangeBlock, LedgerWallet: () => LedgerWallet, - PowGl: () => PowGl, - PowGpu: () => PowGpu, + NanoPowGpu: () => NanoPowGpu, + PowGl: () => NanoPowGl, ReceiveBlock: () => ReceiveBlock, Rolodex: () => Rolodex, Rpc: () => Rpc, @@ -10700,8 +12959,12 @@ __export(main_exports, { init_account(); init_blake2b(); init_block(); -init_powgl(); -init_powgpu(); + +// src/lib/nano-pow/index.ts +init_shaders(); +init_classes(); + +// dist/main.js init_rpc(); // dist/lib/rolodex.js @@ -13028,7 +15291,7 @@ var Bip39Mnemonic = class _Bip39Mnemonic { // dist/lib/wallet.js init_constants(); init_entropy(); -init_pool(); +init_pool2(); init_rpc(); init_safe(); init_workers(); @@ -13068,7 +15331,7 @@ var Wallet = class _Wallet { this.#accounts = []; this.#id = id2; this.#mnemonic = mnemonic ?? null; - this.#poolNanoNacl = new Pool(nano_nacl_default); + this.#poolNanoNacl = new Pool2(nano_nacl_default); this.#safe = new Safe(); this.#seed = seed ?? null; } @@ -13221,7 +15484,7 @@ var Wallet = class _Wallet { }; var Bip44Wallet = class _Bip44Wallet extends Wallet { static #isInternal = false; - static #poolBip44Ckd = new Pool(bip44_ckd_default); + static #poolBip44Ckd = new Pool2(bip44_ckd_default); constructor(id2, seed, mnemonic) { if (!_Bip44Wallet.#isInternal) { throw new Error(`Bip44Wallet cannot be instantiated directly. Use 'await Bip44Wallet.create()' instead.`);