From 3bbdba4b575e9668f022599d220eb304d6d8f588 Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Tue, 3 Dec 2024 21:02:08 -0800 Subject: [PATCH] Restore Node.js polyfills for worker support. --- src/lib/bip44-ckd.ts | 30 ++++++++++++++++++++--------- src/lib/nano-nacl.ts | 34 +++++++++++++++++++++----------- src/lib/pool.ts | 12 ++++++++++-- src/lib/pow.ts | 46 ++++++++++++++++++++++++++++---------------- 4 files changed, 83 insertions(+), 39 deletions(-) diff --git a/src/lib/bip44-ckd.ts b/src/lib/bip44-ckd.ts index 1dfb0f0..3aa91ef 100644 --- a/src/lib/bip44-ckd.ts +++ b/src/lib/bip44-ckd.ts @@ -6,7 +6,18 @@ type ExtendedKey = { chainCode: DataView } -const b = () => { +const b = async () => { + /** + * Polyfill for window methods which do not exist when executing Node.js tests. + */ + if (typeof Window === 'undefined') { + const { isMainThread, parentPort } = await import('node:worker_threads') + if (!isMainThread && parentPort) { + var addEventListener = Object.getPrototypeOf(parentPort).addListener.bind(parentPort) + var postMessage = Object.getPrototypeOf(parentPort).postMessage.bind(parentPort) + } + } + const BIP44_COIN_NANO = 165 const BIP44_PURPOSE = 44 const HARDENED_OFFSET = 0x80000000 @@ -15,14 +26,15 @@ const b = () => { /** * Listens for messages from a calling function. */ - addEventListener('message', (message) => { - const data = JSON.parse(new TextDecoder().decode(message.data ?? message)) - process(data).then(results => { - const buf = new TextEncoder().encode(JSON.stringify(results)).buffer - //@ts-expect-error - postMessage(buf, [buf]) + if (addEventListener != null) { + addEventListener('message', (message: any): void => { + const data = JSON.parse(new TextDecoder().decode(message.data ?? message)) + process(data).then(results => { + const buf = new TextEncoder().encode(JSON.stringify(results)).buffer + postMessage(buf, [buf]) + }) }) - }) + } async function process (data: any[]): Promise { return new Promise(async (resolve) => { @@ -110,7 +122,7 @@ const b = () => { return { nanoCKD } } -export const Bip44Ckd = b() +export const Bip44Ckd = await b() const start = b.toString().indexOf('{') + 1 const end = b.toString().lastIndexOf('return') diff --git a/src/lib/nano-nacl.ts b/src/lib/nano-nacl.ts index 7e04d58..e57e836 100644 --- a/src/lib/nano-nacl.ts +++ b/src/lib/nano-nacl.ts @@ -17,19 +17,31 @@ import { Blake2b } from './blake2b.js' // See for details: https://docs.nano.org/integration-guides/the-basics/ // Original source commit: https://github.com/dchest/tweetnacl-js/blob/71df1d6a1d78236ca3e9f6c788786e21f5a651a6/nacl-fast.js -const n = () => { +const n = async () => { /** - * Listens for messages from a calling function. + * Polyfill for window methods which do not exist when executing Node.js tests. */ - addEventListener('message', message => { - const data = JSON.parse(new TextDecoder().decode(message.data ?? message)) - for (const d of data) { - d.publicKey = convert(d.privateKey) + if (typeof Window === 'undefined') { + const { isMainThread, parentPort } = await import('node:worker_threads') + if (!isMainThread && parentPort) { + var addEventListener = Object.getPrototypeOf(parentPort).addListener.bind(parentPort) + var postMessage = Object.getPrototypeOf(parentPort).postMessage.bind(parentPort) } - const buf = new TextEncoder().encode(JSON.stringify(data)).buffer - //@ts-expect-error - postMessage(buf, [buf]) - }) + } + + /** + * Listens for messages from a calling function. + */ + if (addEventListener != null) { + addEventListener('message', (message: any): void => { + const data = JSON.parse(new TextDecoder().decode(message.data ?? message)) + for (const d of data) { + d.publicKey = convert(d.privateKey) + } + const buf = new TextEncoder().encode(JSON.stringify(data)).buffer + postMessage(buf, [buf]) + }) + } const gf = function (init?: number[]): Float64Array { const r = new Float64Array(16) @@ -850,7 +862,7 @@ const n = () => { return { sign, open, detached, verify, convert } } -export const NanoNaCl = n() +export const NanoNaCl = await n() const start = n.toString().indexOf('{') + 1 const end = n.toString().lastIndexOf('return') diff --git a/src/lib/pool.ts b/src/lib/pool.ts index 5524879..336c5c1 100644 --- a/src/lib/pool.ts +++ b/src/lib/pool.ts @@ -36,7 +36,11 @@ export class Pool { } constructor (fn: string) { - this.#url = URL.createObjectURL(new Blob([fn], { type: 'text/javascript' })) + if (typeof Window === 'undefined') { + this.#url = fn + } else { + this.#url = URL.createObjectURL(new Blob([fn], { type: 'text/javascript' })) + } for (let i = this.#cores; i > 0; i--) { const thread = { isBusy: false, @@ -104,6 +108,10 @@ export class Pool { } dismiss (): void { - URL.revokeObjectURL(this.#url) + if (typeof Window === 'undefined') { + this.#threads.forEach(thread => thread.worker.terminate()) + } else { + URL.revokeObjectURL(this.#url) + } } } diff --git a/src/lib/pow.ts b/src/lib/pow.ts index 94cc3dc..9065386 100644 --- a/src/lib/pow.ts +++ b/src/lib/pow.ts @@ -1,27 +1,39 @@ // SPDX-FileCopyrightText: 2024 Chris Duncan // SPDX-License-Identifier: GPL-3.0-or-later -const p = () => { +const p = async () => { + /** + * Polyfill for window methods which do not exist when executing Node.js tests. + */ + if (typeof Window === 'undefined') { + const { isMainThread, parentPort } = await import('node:worker_threads') + if (!isMainThread && parentPort) { + var addEventListener = Object.getPrototypeOf(parentPort).addListener.bind(parentPort) + var postMessage = Object.getPrototypeOf(parentPort).postMessage.bind(parentPort) + } + } + const SEND_THRESHOLD = '0xfffffff8' /** * Listens for messages from a calling function. */ - addEventListener('message', message => { - const data = JSON.parse(new TextDecoder().decode(message.data ?? message)) - for (const d of data) { - if (d === 'stop') { - close() - postMessage(new ArrayBuffer(0)) - } else { - find(d.hash, d.threshold ?? SEND_THRESHOLD).then(nonce => { - d.work = nonce - const buf = new TextEncoder().encode(JSON.stringify(data)).buffer - //@ts-expect-error - postMessage(buf, [buf]) - }) + if (addEventListener != null) { + addEventListener('message', (message: any): void => { + const data = JSON.parse(new TextDecoder().decode(message.data ?? message)) + for (const d of data) { + if (d === 'stop') { + close() + postMessage(new ArrayBuffer(0)) + } else { + find(d.hash, d.threshold ?? SEND_THRESHOLD).then(nonce => { + d.work = nonce + const buf = new TextEncoder().encode(JSON.stringify(data)).buffer + postMessage(buf, [buf]) + }) + } } - } - }) + }) + } async function find (hash: string, threshold: string = SEND_THRESHOLD): Promise { return new Promise(resolve => { @@ -364,7 +376,7 @@ const p = () => { return { find } } -export const Pow = p() +export const Pow = await p() const start = p.toString().indexOf('{') + 1 const end = p.toString().lastIndexOf('return') -- 2.34.1