From e0763f6541c84b488d7d20bd91b68b20de5cbc58 Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Sun, 20 Jul 2025 03:31:43 -0700 Subject: [PATCH] Refactor types for data passed to workers and reduce back to one parameter with buffers extracted for transfer. Rename worker queue file for clarity. Get node test runner half working. --- src/lib/account.ts | 44 ++++++------- src/lib/blake2b.ts | 6 +- src/lib/block.ts | 7 +-- src/lib/rpc.ts | 13 ++-- src/lib/tools.ts | 22 +++---- src/lib/wallets/bip44-wallet.ts | 6 +- src/lib/wallets/wallet.ts | 15 ++--- src/lib/workers/bip44-ckd.ts | 22 ++++--- src/lib/workers/index.ts | 2 +- src/lib/workers/nano-nacl.ts | 20 ++++-- src/lib/workers/safe.ts | 41 +++++++----- src/lib/workers/worker-interface.ts | 46 +++++++------- src/lib/workers/{queue.ts => worker-queue.ts} | 40 ++++++------ src/types.d.ts | 12 ++-- test/GLOBALS.mjs | 5 +- test/perf.account.js | 6 +- test/perf.block.js | 4 +- test/perf.wallet.js | 4 +- test/test.calculate-pow.mjs | 4 +- test/test.ledger.mjs | 4 +- test/test.refresh-accounts.mjs | 4 +- test/test.runner-check.mjs | 63 ++++++++++--------- test/test.tools.mjs | 10 +-- 23 files changed, 199 insertions(+), 201 deletions(-) rename src/lib/workers/{queue.ts => worker-queue.ts} (67%) diff --git a/src/lib/account.ts b/src/lib/account.ts index 8674c5e..7367d0f 100644 --- a/src/lib/account.ts +++ b/src/lib/account.ts @@ -6,7 +6,7 @@ import { ChangeBlock, ReceiveBlock, SendBlock } from './block' import { ACCOUNT_KEY_BYTE_LENGTH, ACCOUNT_KEY_HEX_LENGTH, ALPHABET, PREFIX, PREFIX_LEGACY } from './constants' import { base32, bytes, hex, utf8 } from './convert' import { Rpc } from './rpc' -import { Data, Key, KeyPair } from '#types' +import { Key, KeyPair, NamedData } from '#types' import { NanoNaClWorker, SafeWorker } from '#workers' /** @@ -253,15 +253,13 @@ export class Account { async sign (block: ChangeBlock | ReceiveBlock | SendBlock, password: Key): Promise { if (typeof password === 'string') password = utf8.toBytes(password) try { - const headers = { - method: 'detached' - } - const result = await NanoNaClWorker.assign(headers, { + const { signature } = await NanoNaClWorker.assign({ + method: 'detached', privateKey: (await this.#export(password)).buffer, msg: hex.toBytes(block.hash).buffer }) - block.signature = result - return result + block.signature = bytes.toHex(new Uint8Array(signature)) + return block.signature } catch (err) { throw new Error(`Failed to sign block`, { cause: err }) } finally { @@ -329,15 +327,13 @@ export class Account { throw new Error('Password must be string or bytes') } try { - const headers = { + const response = await SafeWorker.assign({ method: 'get', name: this.publicKey, - store: 'Account' - } - const response = await SafeWorker.assign(headers, { + store: 'Account', password: password.buffer }) - return new Uint8Array(response[this.publicKey] as ArrayBuffer) + return new Uint8Array(response[this.publicKey]) } catch (err) { throw new Error(`Failed to export private key for Account ${this.address}`, { cause: err }) } finally { @@ -361,7 +357,11 @@ export class Account { } const accounts: Account[] = [] - const privateAccounts: Data = {} + const privateAccounts: NamedData = { + method: 'set', + store: 'Account', + password: password.buffer + } for (let keypair of keypairs) { let { index, privateKey } = keypair if (index == null) { @@ -370,14 +370,13 @@ export class Account { this.#validateKey(privateKey) if (typeof privateKey === 'string') privateKey = hex.toBytes(privateKey) try { - const headers = { - method: 'convert' - } - const publicKey = await NanoNaClWorker.assign(headers, { + const result = await NanoNaClWorker.assign({ + method: 'convert', privateKey: new Uint8Array(privateKey).buffer }) - privateAccounts[publicKey] = privateKey.buffer - const address = this.#keyToAddress(hex.toBytes(publicKey)) + const publicKey = new Uint8Array(result.publicKey) + privateAccounts[bytes.toHex(publicKey)] = privateKey.buffer + const address = this.#keyToAddress(publicKey) this.#isInternal = true accounts.push(new this(address, publicKey, index)) } catch (err) { @@ -386,12 +385,7 @@ export class Account { } try { - const headers = { - method: 'set', - store: 'Account' - } - privateAccounts.password = password.buffer - const isLocked = await SafeWorker.assign(headers, privateAccounts) + const isLocked = await SafeWorker.assign(privateAccounts) if (!isLocked) { throw null } diff --git a/src/lib/blake2b.ts b/src/lib/blake2b.ts index b9e25df..c4f07a1 100644 --- a/src/lib/blake2b.ts +++ b/src/lib/blake2b.ts @@ -264,10 +264,10 @@ export class Blake2b { return this } - digest (): Uint8Array + digest (): Uint8Array digest (out: 'hex'): string - digest (out: 'binary' | Uint8Array): Uint8Array - digest (out?: 'binary' | 'hex' | Uint8Array): string | Uint8Array { + digest (out: 'binary' | Uint8Array): Uint8Array + digest (out?: 'binary' | 'hex' | Uint8Array): string | Uint8Array { 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`) diff --git a/src/lib/block.ts b/src/lib/block.ts index 1eec2c9..bc2d2b6 100644 --- a/src/lib/block.ts +++ b/src/lib/block.ts @@ -191,14 +191,13 @@ abstract class Block { throw new Error('Provide a key for block signature verification.') } try { - const headers = { - method: 'verify' - } - return await NanoNaClWorker.assign(headers, { + const { isVerified } = await NanoNaClWorker.assign({ + method: 'verify', msg: hex.toBytes(this.hash).buffer, signature: hex.toBytes(this.signature ?? '').buffer, publicKey: hex.toBytes(key).buffer }) + return isVerified } catch (err) { throw new Error(`Failed to derive public key from private key`, { cause: err }) } diff --git a/src/lib/rpc.ts b/src/lib/rpc.ts index be6fc4c..1fc8d25 100644 --- a/src/lib/rpc.ts +++ b/src/lib/rpc.ts @@ -19,12 +19,13 @@ export class Rpc { } /** - * - * @param {string} action - Nano protocol RPC call to execute - * @param {object} [data] - JSON to send to the node as defined by the action - * @returns {Promise} JSON-formatted RPC results from the node - */ - async call (action: string, data?: { [key: string]: any }): Promise { + * Sends a nano RPC call to a node endpoint. + * + * @param {string} action - Nano protocol RPC call to execute + * @param {object} [data] - JSON to send to the node as defined by the action + * @returns {Promise} JSON-formatted RPC results from the node + */ + async call (action: string, data?: { [key: string]: unknown }): Promise { var process: any = process || null this.#validate(action) const headers: { [key: string]: string } = {} diff --git a/src/lib/tools.ts b/src/lib/tools.ts index eeeaac2..a61bece 100644 --- a/src/lib/tools.ts +++ b/src/lib/tools.ts @@ -17,7 +17,8 @@ type SweepResult = { message: string } -function hash (data: string | string[], encoding?: 'hex', format?: 'hex'): string | Uint8Array { +function hash (data: string | string[], encoding?: 'hex'): Uint8Array +function hash (data: string | string[], encoding?: 'hex', format?: 'hex'): string | Uint8Array { if (!Array.isArray(data)) data = [data] const hash = new Blake2b(32) if (encoding === 'hex') { @@ -90,15 +91,13 @@ export async function convert (amount: bigint | string, inputUnit: string, outpu */ export async function sign (key: Key, ...input: string[]): Promise { if (typeof key === 'string') key = hex.toBytes(key) - let signature: string try { - const headers = { - method: 'detached' - } - return await NanoNaClWorker.assign(headers, { + const { signature } = await NanoNaClWorker.assign({ + method: 'detached', privateKey: key.buffer, - msg: (hash(input) as Uint8Array).buffer + msg: hash(input).buffer }) + return bytes.toHex(new Uint8Array(signature)) } catch (err) { throw new Error(`Failed to sign message with private key`, { cause: err }) } finally { @@ -178,14 +177,13 @@ export async function sweep ( export async function verify (key: Key, signature: string, ...input: string[]): Promise { if (typeof key === 'string') key = hex.toBytes(key) try { - const headers = { - method: 'verify' - } - return await NanoNaClWorker.assign(headers, { - msg: (hash(input) as Uint8Array).buffer, + const { isVerified } = await NanoNaClWorker.assign({ + method: 'verify', + msg: hash(input).buffer, signature: hex.toBytes(signature).buffer, publicKey: new Uint8Array(key).buffer }) + return isVerified } catch (err) { throw new Error('Failed to verify signature', { cause: err }) } finally { diff --git a/src/lib/wallets/bip44-wallet.ts b/src/lib/wallets/bip44-wallet.ts index 8ea9276..1a1e368 100644 --- a/src/lib/wallets/bip44-wallet.ts +++ b/src/lib/wallets/bip44-wallet.ts @@ -212,10 +212,8 @@ export class Bip44Wallet extends Wallet { * @returns {Promise} */ async ckd (indexes: number[]): Promise { - const headers = { - indexes - } - const results = await Bip44CkdWorker.assign(headers, { + const results = await Bip44CkdWorker.assign({ + indexes, seed: hex.toBytes(this.seed).buffer }) const privateKeys: KeyPair[] = [] diff --git a/src/lib/wallets/wallet.ts b/src/lib/wallets/wallet.ts index d6069b4..f14029b 100644 --- a/src/lib/wallets/wallet.ts +++ b/src/lib/wallets/wallet.ts @@ -7,7 +7,7 @@ import { ADDRESS_GAP } from '#src/lib/constants.js' import { bytes, hex, utf8 } from '#src/lib/convert.js' import { Entropy } from '#src/lib/entropy.js' import { Rpc } from '#src/lib/rpc.js' -import { Key, KeyPair } from '#types' +import { Key, KeyPair, NamedData } from '#types' import { SafeWorker } from '#workers' /** @@ -146,6 +146,7 @@ export abstract class Wallet { method: 'destroy', name: this.id }) + // NODE: await SafeWorker.assign({ method: 'STOP' }) } /** @@ -169,11 +170,9 @@ export abstract class Wallet { seed: this.seed }) const encoded = utf8.toBytes(serialized) - const headers = { + const success = await SafeWorker.assign({ method: 'set', - store: 'Wallet' - } - const success = await SafeWorker.assign(headers, { + store: 'Wallet', [this.id]: encoded.buffer, password: password.buffer }) @@ -232,15 +231,13 @@ export abstract class Wallet { throw new Error('Failed to unlock wallet') } try { - const headers = { + const response = await SafeWorker.assign({ method: 'get', name: this.id, store: 'Wallet', - } - const response = await SafeWorker.assign(headers, { password: password.buffer }) - const decoded = bytes.toUtf8(response[this.id]) + const decoded = bytes.toUtf8(new Uint8Array(response[this.id])) const deserialized = JSON.parse(decoded) let { id, mnemonic, seed } = deserialized if (id == null) { diff --git a/src/lib/workers/bip44-ckd.ts b/src/lib/workers/bip44-ckd.ts index 2076cca..da67264 100644 --- a/src/lib/workers/bip44-ckd.ts +++ b/src/lib/workers/bip44-ckd.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later import { WorkerInterface } from './worker-interface' -import { Data, Headers } from '#types' +import { Data, NamedData } from '#types' type ExtendedKey = { privateKey: DataView @@ -19,14 +19,22 @@ export class Bip44Ckd extends WorkerInterface { this.listen() } - static async work (headers: Headers, data: Data): Promise { - let { coin, indexes } = headers - let { seed } = data - if (coin != null && (typeof coin !== 'number' || !Number.isInteger(coin))) { + static async work (data: NamedData): Promise> { + if (data.coin != null && (typeof data.coin !== 'number' || !Number.isInteger(data.coin))) { throw new TypeError('BIP-44 coin derivation level must be an integer') } - if (!Array.isArray(indexes)) indexes = [indexes] - const privateKeys: Data = {} + if (!Array.isArray(data.indexes) || data.indexes.some(i => !Number.isInteger(i))) { + throw new TypeError('BIP-44 account indexes must be an array of integers') + } + if (!(data.seed instanceof ArrayBuffer)) { + throw new TypeError('BIP-44 seed must be an ArrayBuffer') + } + const coin: number = data.coin + const indexes = Array.isArray(data.indexes) + ? data.indexes + : [data.indexes] + const seed = data.seed + const privateKeys: NamedData = {} for (const i of indexes) { if (typeof i !== 'number' || !Number.isInteger(i)) { throw new TypeError('BIP-44 account derivation level must be an integer') diff --git a/src/lib/workers/index.ts b/src/lib/workers/index.ts index e363171..2b8c811 100644 --- a/src/lib/workers/index.ts +++ b/src/lib/workers/index.ts @@ -1,4 +1,4 @@ // SPDX-FileCopyrightText: 2025 Chris Duncan // SPDX-License-Identifier: GPL-3.0-or-later -export { Bip44CkdWorker, NanoNaClWorker, SafeWorker } from './queue' +export { Bip44CkdWorker, NanoNaClWorker, SafeWorker } from './worker-queue' diff --git a/src/lib/workers/nano-nacl.ts b/src/lib/workers/nano-nacl.ts index d6b1ed1..58c7616 100644 --- a/src/lib/workers/nano-nacl.ts +++ b/src/lib/workers/nano-nacl.ts @@ -6,7 +6,7 @@ import { WorkerInterface } from './worker-interface' import { Blake2b } from '#src/lib/blake2b.js' import { default as Convert, bytes, hex } from '#src/lib/convert.js' -import { Data, Headers, Key } from '#types' +import { Data, NamedData, Key } from '#types' /** * Ported in 2014 by Dmitry Chestnykh and Devi Mandiri. @@ -26,8 +26,16 @@ export class NanoNaCl extends WorkerInterface { this.listen() } - static async work (headers: Headers, data: Data): Promise { - const { method } = headers + static async work (data: NamedData): Promise { + if (typeof data.method !== 'string' + || !(data.msg == null || data.msg instanceof ArrayBuffer) + || !(data.privateKey == null || data.privateKey instanceof ArrayBuffer) + || !(data.publicKey == null || data.publicKey instanceof ArrayBuffer) + || !(data.signature == null || data.signature instanceof ArrayBuffer) + ) { + throw new TypeError('Invalid NanoNaCl input') + } + const method = data.method const msg = new Uint8Array(data.msg) const privateKey = new Uint8Array(data.privateKey) const publicKey = new Uint8Array(data.publicKey) @@ -35,13 +43,13 @@ export class NanoNaCl extends WorkerInterface { try { switch (method) { case 'convert': { - return bytes.toHex(await this.convert(privateKey)) + return { publicKey: (await this.convert(privateKey)).buffer } } case 'detached': { - return bytes.toHex(await this.detached(msg, privateKey)) + return { signature: (await this.detached(msg, privateKey)).buffer } } case 'verify': { - return await this.verify(msg, signature, publicKey) + return { isVerified: await this.verify(msg, signature, publicKey) } } default: { throw new TypeError(`unknown NanoNaCl method ${method}`) diff --git a/src/lib/workers/safe.ts b/src/lib/workers/safe.ts index 824599e..5ddbb8b 100644 --- a/src/lib/workers/safe.ts +++ b/src/lib/workers/safe.ts @@ -7,7 +7,7 @@ import { WorkerInterface } from './worker-interface' import { PBKDF2_ITERATIONS } from '#src/lib/constants.js' import { default as Convert, bytes } from '#src/lib/convert.js' import { Entropy } from '#src/lib/entropy.js' -import { Data, Headers, SafeRecord } from '#types' +import { NamedData, SafeRecord } from '#types' /** * Encrypts and stores data in the browser using IndexedDB. @@ -22,25 +22,33 @@ export class Safe extends WorkerInterface { this.listen() } - static async work (headers: Headers, data: Data): Promise { - const { method, name, store } = headers - const password = data?.password - if (data != null) delete data.password + static async work (data: NamedData): Promise> { + const { method, name, store } = data + if (typeof method !== 'string') { + throw new TypeError('Invalid method') + } + delete data.method + if (name != null && typeof name !== 'string') { + throw new TypeError('Invalid name') + } + delete data.name + if (typeof store !== 'string') { + throw new TypeError('Invalid store') + } + delete data.store + const password = data.password + delete data.password this.#storage = await this.#open(this.DB_NAME) - let result try { switch (method) { case 'set': { - result = await this.set(data, store, password) - break + return { result: await this.set(data, store, password) } } case 'get': { - result = await this.get(name, store, password) - break + return await this.get(name, store, password) } case 'destroy': { - result = await this.destroy(name, store) - break + return { result: await this.destroy(name, store) } } default: { throw new Error(`unknown Safe method ${method}`) @@ -50,7 +58,6 @@ export class Safe extends WorkerInterface { console.log(err) throw new Error('Safe error', { cause: err }) } - return result } /** @@ -68,7 +75,7 @@ export class Safe extends WorkerInterface { /** * Encrypts data with a password byte array and stores it in the Safe. */ - static async set (data: Data | unknown, store: string | unknown, password: ArrayBuffer | unknown): Promise { + static async set (data: NamedData | unknown, store: string | unknown, password: ArrayBuffer | unknown): Promise { this.#isDataValid(data) if (typeof store !== 'string' || store === '') { throw new Error('Invalid database store name') @@ -103,7 +110,7 @@ export class Safe extends WorkerInterface { /** * Retrieves data from the Safe and decrypts it with a password byte array. */ - static async get (name: string | string[] | unknown, store: string | unknown, password: ArrayBuffer | unknown): Promise { + static async get (name: string | string[] | unknown, store: string | unknown, password: ArrayBuffer | unknown): Promise> { const names = Array.isArray(name) ? name : [name] if (names.some(v => typeof v !== 'string')) { throw new Error('Invalid fields') @@ -116,7 +123,7 @@ export class Safe extends WorkerInterface { throw new Error('Invalid password') } - const results: Data = {} + const results: NamedData = {} try { const records: SafeRecord[] = await this.#get(fields, store) if (records == null || records.length === 0) { @@ -184,7 +191,7 @@ export class Safe extends WorkerInterface { }) } - static #isDataValid (data: unknown): asserts data is Data { + static #isDataValid (data: unknown): asserts data is { [key: string]: ArrayBuffer } { if (typeof data !== 'object') { throw new Error('Invalid data') } diff --git a/src/lib/workers/worker-interface.ts b/src/lib/workers/worker-interface.ts index 9b801e6..76b03d7 100644 --- a/src/lib/workers/worker-interface.ts +++ b/src/lib/workers/worker-interface.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later import { parentPort } from 'node:worker_threads' -import { Data, Headers } from '#types' +import { Data, NamedData } from '#types' /** * Provides basic worker event messaging to extending classes. @@ -32,22 +32,16 @@ export abstract class WorkerInterface { * function signature and providing their own processing call in the try-catch * block. * - * @param {Header} headers - Flat object of header data - * @param {Data} data - String keys for ArrayBuffer values to transfer and process + * @param {NamedData} data - Flat object of data key-value pairs * @returns Promise for processed data */ - static async work (headers: Headers | null, data?: Data): Promise { - return new Promise(async (resolve, reject): Promise => { + static work (data: NamedData): Promise { + return new Promise((resolve, reject): void => { try { - let x, y = new ArrayBuffer(0) - if (headers != null) { - const { sample } = headers - x = sample - } - if (data != null) { - const { buf } = data - y = buf + if (data == null) { + throw new Error('Missing data') } + const { x, y } = data resolve({ x, y }) } catch (err) { reject(err) @@ -58,15 +52,15 @@ export abstract class WorkerInterface { /** * Transfers buffers of worker results back to the main thread. * - * @param {Headers} results - Key-value pairs of processed data + * @param {NamedData} data - Key-value pairs of processed data */ - static report (results: Headers): void { + static report (data: NamedData): void { const buffers = [] try { - if (typeof results === 'object') { - for (const d of Object.keys(results)) { - if (results[d] instanceof ArrayBuffer) { - buffers.push(results[d]) + if (typeof data === 'object') { + for (const d of Object.keys(data)) { + if (data[d] instanceof ArrayBuffer) { + buffers.push(data[d]) } } } @@ -75,8 +69,8 @@ export abstract class WorkerInterface { throw new Error('Failed to report results', { cause: err }) } //@ts-expect-error - BROWSER: postMessage(results, buffers) - NODE: parentPort?.postMessage(results, buffers) + BROWSER: postMessage(data, buffers) + NODE: parentPort?.postMessage({ data }, buffers) } /** @@ -91,12 +85,14 @@ export abstract class WorkerInterface { */ static listen (): void { const listener = async (message: MessageEvent): Promise => { - const { name, headers, data } = message.data - if (name === 'STOP') { - close() + // console.log(message) + const { data } = message + if (data.method === 'STOP') { this.report({}) + BROWSER: close() + NODE: process.exit() } else { - this.work(headers, data).then(this.report).catch(this.report) + this.work(data).then(this.report).catch(this.report) } } BROWSER: addEventListener('message', listener) diff --git a/src/lib/workers/queue.ts b/src/lib/workers/worker-queue.ts similarity index 67% rename from src/lib/workers/queue.ts rename to src/lib/workers/worker-queue.ts index 5a49dac..6cea63b 100644 --- a/src/lib/workers/queue.ts +++ b/src/lib/workers/worker-queue.ts @@ -5,12 +5,11 @@ import { Worker as NodeWorker } from 'node:worker_threads' import { default as bip44 } from './bip44-ckd' import { default as nacl } from './nano-nacl' import { default as safe } from './safe' -import { Data, Headers } from '#types' +import { Data, NamedData } from '#types' type Task = { id: number - headers: Headers | null - data?: Data + data: NamedData reject: (value: any) => void resolve: (value: any) => void } @@ -18,9 +17,9 @@ type Task = { /** * Processes a queue of tasks using Web Workers. */ -export class Queue { - static #instances: Queue[] = [] - static get instances (): Queue[] { return this.#instances } +export class WorkerQueue { + static #instances: WorkerQueue[] = [] + static get instances (): WorkerQueue[] { return this.#instances } #job?: Task #isIdle: boolean @@ -50,15 +49,15 @@ export class Queue { NODE: this.#worker.on('message', message => { this.#report(message) }) - Queue.#instances.push(this) + WorkerQueue.#instances.push(this) } - async assign (headers: Headers | null, data?: Data): Promise { - return this.#assign(task => this.#queue.push(task), headers, data) + async assign (data: NamedData): Promise> { + return this.#assign(data, task => this.#queue.push(task)) } - async prioritize (headers: Headers | null, data?: Data): Promise { - return this.#assign(task => this.#queue.unshift(task), headers, data) + async prioritize (data: NamedData): Promise> { + return this.#assign(data, task => this.#queue.unshift(task)) } terminate (): void { @@ -66,11 +65,10 @@ export class Queue { this.#worker.terminate() } - async #assign (enqueue: (task: Task) => number, headers: Headers | null, data?: Data): Promise { + async #assign (data: NamedData, enqueue: (task: Task) => number): Promise> { return new Promise(async (resolve, reject): Promise => { const task: Task = { id: performance.now(), - headers, data, resolve, reject @@ -84,16 +82,16 @@ export class Queue { this.#job = this.#queue.shift() this.#isIdle = this.#job == null if (this.#job != null) { - const { id, headers, data, reject } = this.#job + const { data, reject } = this.#job try { const buffers: ArrayBuffer[] = [] - if (data != null) { - for (let d of Object.keys(data)) { + for (let d of Object.keys(data)) { + if (data[d] instanceof ArrayBuffer) { buffers.push(data[d]) } } - BROWSER: this.#worker.postMessage({ headers, data }, buffers) - NODE: this.#worker.postMessage({ data: { headers, data } }, buffers) + BROWSER: this.#worker.postMessage(data, buffers) + NODE: this.#worker.postMessage({ data }, buffers) } catch (err) { reject(err) } @@ -115,6 +113,6 @@ export class Queue { } } -export const Bip44CkdWorker = new Queue(bip44) -export const NanoNaClWorker = new Queue(nacl) -export const SafeWorker = new Queue(safe) +export const Bip44CkdWorker = new WorkerQueue(bip44) +export const NanoNaClWorker = new WorkerQueue(nacl) +export const SafeWorker = new WorkerQueue(safe) diff --git a/src/types.d.ts b/src/types.d.ts index 50a9415..d3a73b7 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -276,12 +276,10 @@ export declare class ChangeBlock extends Block { constructor (account: Account | string, balance: string, representative: Account | string, frontier: string, work?: string) } -export type Data = { - [key: string]: ArrayBuffer -} +export type Data = boolean | number[] | string | ArrayBuffer -export type Headers = { - [key: string]: any +export type NamedData = { + [key: string]: T } export type Key = string | Uint8Array @@ -304,8 +302,8 @@ export declare class Queue { * @param {number} [count=1] - Integer between 1 and CPU thread count shared among all Pools */ constructor (worker: string) - assign (headers: Headers | null, data?: Data): Promise - prioritize (headers: Headers | null, data?: Data): Promise + assign (data: NamedData): Promise + prioritize (data: NamedData): Promise terminate (): void } export declare const Bip44CkdWorker: Queue diff --git a/test/GLOBALS.mjs b/test/GLOBALS.mjs index ea87e35..96da91c 100644 --- a/test/GLOBALS.mjs +++ b/test/GLOBALS.mjs @@ -1,13 +1,12 @@ // SPDX-FileCopyrightText: 2025 Chris Duncan // SPDX-License-Identifier: GPL-3.0-or-later +export { env } from '../env.mjs' import { Queue } from './QUEUE.mjs' -import { process } from '../env.mjs' const queue = new Queue() -export { process } -export const isNode = process.versions?.node != null +export const isNode = globalThis !== globalThis.window let NodeTestSuite, NodeTestTest if (isNode) { diff --git a/test/perf.account.js b/test/perf.account.js index e33296a..1e1b948 100644 --- a/test/perf.account.js +++ b/test/perf.account.js @@ -7,7 +7,7 @@ import { assert, stats, suite, test } from './GLOBALS.mjs' import { NANO_TEST_VECTORS } from './VECTORS.js' import { Bip44Wallet, Blake2bWallet } from '../dist/main.min.js' -await suite('Account performance', async () => { +await suite('Account performance', { skip: true }, async () => { const COUNT = 0x200 await test(`Time to create ${COUNT} BIP-44 accounts`, async () => { @@ -18,7 +18,7 @@ await suite('Account performance', async () => { const end = performance.now() console.log(`Total: ${end - start} ms`) console.log(`Average: ${(end - start) / COUNT} ms`) - assert.equals(accounts.length, COUNT) + assert.equal(accounts.length, COUNT) await wallet.destroy() }) @@ -30,7 +30,7 @@ await suite('Account performance', async () => { const end = performance.now() console.log(`Total: ${end - start} ms`) console.log(`Average: ${(end - start) / COUNT} ms`) - assert.equals(accounts.length, COUNT) + assert.equal(accounts.length, COUNT) await wallet.destroy() }) diff --git a/test/perf.block.js b/test/perf.block.js index 567a29f..20b8350 100644 --- a/test/perf.block.js +++ b/test/perf.block.js @@ -3,11 +3,11 @@ 'use strict' -import { assert, stats, suite, test } from './GLOBALS.mjs' +import { stats, suite, test } from './GLOBALS.mjs' import { NANO_TEST_VECTORS } from './VECTORS.js' import { SendBlock } from '../dist/main.min.js' -await suite('Block performance', async () => { +await suite('Block performance', { skip: true }, async () => { const COUNT = 0x200 await test(`libnemo: Time to calculate proof-of-work for a send block ${COUNT} times`, { skip: true }, async () => { diff --git a/test/perf.wallet.js b/test/perf.wallet.js index e1dd75c..b58adc7 100644 --- a/test/perf.wallet.js +++ b/test/perf.wallet.js @@ -3,11 +3,11 @@ 'use strict' -import { assert, stats, suite, test } from './GLOBALS.mjs' +import { stats, suite, test } from './GLOBALS.mjs' import { NANO_TEST_VECTORS } from './VECTORS.js' import { Bip44Wallet, Blake2bWallet } from '../dist/main.min.js' -await suite(`Wallet performance`, async () => { +await suite(`Wallet performance`, { skip: true }, async () => { const COUNT = 0x20 await test(`Time to create ${COUNT} BIP-44 wallets`, async () => { diff --git a/test/test.calculate-pow.mjs b/test/test.calculate-pow.mjs index 2844d50..29b6bdb 100644 --- a/test/test.calculate-pow.mjs +++ b/test/test.calculate-pow.mjs @@ -3,11 +3,11 @@ 'use strict' -import { assert, suite, test } from './GLOBALS.mjs' +import { assert, isNode, suite, test } from './GLOBALS.mjs' import { NANO_TEST_VECTORS } from './VECTORS.js' import { SendBlock, Blake2b } from '../dist/main.min.js' -await suite('Calculate proof-of-work', async () => { +await suite('Calculate proof-of-work', { skip: isNode }, async () => { await test('SendBlock PoW', async () => { const block = new SendBlock( diff --git a/test/test.ledger.mjs b/test/test.ledger.mjs index 28c2173..812dda7 100644 --- a/test/test.ledger.mjs +++ b/test/test.ledger.mjs @@ -3,11 +3,11 @@ 'use strict' -import { assert, click, isNode, process, suite, test } from './GLOBALS.mjs' +import { assert, click, env, isNode, suite, test } from './GLOBALS.mjs' import { NANO_TEST_VECTORS } from './VECTORS.js' import { Account, LedgerWallet, ReceiveBlock, Rpc, SendBlock } from '../dist/main.min.js' -const rpc = new Rpc(process.env.NODE_URL ?? '', process.env.API_KEY_NAME) +const rpc = new Rpc(env.NODE_URL ?? '', env.API_KEY_NAME) /** * HID interactions require user gestures, so to reduce clicks, the variables diff --git a/test/test.refresh-accounts.mjs b/test/test.refresh-accounts.mjs index 5d07051..409a077 100644 --- a/test/test.refresh-accounts.mjs +++ b/test/test.refresh-accounts.mjs @@ -3,11 +3,11 @@ 'use strict' -import { assert, process, suite, test } from './GLOBALS.mjs' +import { assert, env, suite, test } from './GLOBALS.mjs' import { NANO_TEST_VECTORS } from './VECTORS.js' import { Account, Bip44Wallet, Rpc } from '../dist/main.min.js' -const rpc = new Rpc(process.env.NODE_URL ?? '', process.env.API_KEY_NAME) +const rpc = new Rpc(env.NODE_URL ?? '', env.API_KEY_NAME) await suite('Refreshing account info', { skip: true }, async () => { diff --git a/test/test.runner-check.mjs b/test/test.runner-check.mjs index 2295d30..1f2bd9e 100644 --- a/test/test.runner-check.mjs +++ b/test/test.runner-check.mjs @@ -1,42 +1,45 @@ // SPDX-FileCopyrightText: 2025 Chris Duncan // SPDX-License-Identifier: GPL-3.0-or-later -import { failures, passes, suite, test } from './GLOBALS.mjs' +import { failures, isNode, passes, suite, test } from './GLOBALS.mjs' -await suite('TEST RUNNER CHECK', async () => { - await new Promise(r => setTimeout(r, 0)) +if (!isNode) { + await suite('TEST RUNNER CHECK', async () => { + await new Promise(r => setTimeout(r, 0)) - console.assert(failures.length === 0) - console.assert(passes.length === 0) + console.assert(failures.length === 0) + console.assert(passes.length === 0) - //@ts-expect-error - await test('promise should pass', new Promise(resolve => resolve(null))) - console.assert(failures.some(call => /.*promise should pass.*/.test(call[0])) === false, `good promise errored`) - console.assert(passes.some(call => /.*promise should pass.*/.test(call)) === true, `good promise not logged`) + //@ts-expect-error + await test('promise should pass', new Promise(resolve => resolve(null))) + console.assert(failures.some(call => /.*promise should pass.*/.test(call[0])) === false, `good promise errored`) + console.assert(passes.some(call => /.*promise should pass.*/.test(call)) === true, `good promise not logged`) - //@ts-expect-error - await test('promise should fail', new Promise((resolve, reject) => reject('FAILURE EXPECTED HERE'))) - console.assert(failures.some(call => /.*promise should fail.*/.test(call)) === true, `bad promise not errored`) - console.assert(passes.some(call => /.*promise should fail.*/.test(call)) === false, 'bad promise logged') + //@ts-expect-error + await test('promise should fail', new Promise((resolve, reject) => reject('FAILURE EXPECTED HERE'))) + console.assert(failures.some(call => /.*promise should fail.*/.test(call)) === true, `bad promise not errored`) + console.assert(passes.some(call => /.*promise should fail.*/.test(call)) === false, 'bad promise logged') - await test('async should pass', async () => {}) - console.assert(failures.some(call => /.*async should pass.*/.test(call)) === false, 'good async errored') - console.assert(passes.some(call => /.*async should pass.*/.test(call)) === true, 'good async not logged') + await test('async should pass', async () => {}) + console.assert(failures.some(call => /.*async should pass.*/.test(call)) === false, 'good async errored') + console.assert(passes.some(call => /.*async should pass.*/.test(call)) === true, 'good async not logged') - await test('async should fail', async () => { throw new Error('FAILURE EXPECTED HERE') }) - console.assert(failures.some(call => /.*async should fail.*/.test(call)) === true, 'bad async not errored') - console.assert(passes.some(call => /.*async should fail.*/.test(call)) === false, 'bad async logged') + await test('async should fail', async () => { throw new Error('FAILURE EXPECTED HERE') }) + console.assert(failures.some(call => /.*async should fail.*/.test(call)) === true, 'bad async not errored') + console.assert(passes.some(call => /.*async should fail.*/.test(call)) === false, 'bad async logged') - await test('function should pass', () => {}) - console.assert(failures.some(call => /.*function should pass.*/.test(call)) === false, 'good function errored') - console.assert(passes.some(call => /.*function should pass.*/.test(call)) === true, 'good function not logged') + await test('function should pass', () => {}) + console.assert(failures.some(call => /.*function should pass.*/.test(call)) === false, 'good function errored') + console.assert(passes.some(call => /.*function should pass.*/.test(call)) === true, 'good function not logged') - //@ts-expect-error - await test('function should fail', 'FAILURE EXPECTED HERE') - console.assert(failures.some(call => /.*function should fail.*/.test(call)) === true, 'bad function not errored') - console.assert(passes.some(call => /.*function should fail.*/.test(call)) === false, 'bad function logged') + //@ts-expect-error + await test('function should fail', 'FAILURE EXPECTED HERE') + console.assert(failures.some(call => /.*function should fail.*/.test(call)) === true, 'bad function not errored') + console.assert(passes.some(call => /.*function should fail.*/.test(call)) === false, 'bad function logged') - failures.splice(0) - passes.splice(0) -}) -console.log(`%cTEST RUNNER CHECK DONE`, 'font-weight:bold') + failures.splice(0) + passes.splice(0) + }) + + console.log(`%cTEST RUNNER CHECK DONE`, 'font-weight:bold') +} diff --git a/test/test.tools.mjs b/test/test.tools.mjs index 903a5ec..e5a4599 100644 --- a/test/test.tools.mjs +++ b/test/test.tools.mjs @@ -3,17 +3,11 @@ 'use strict' -import { assert, suite, test } from './GLOBALS.mjs' +import { assert, env, suite, test } from './GLOBALS.mjs' import { RAW_MAX, NANO_TEST_VECTORS } from './VECTORS.js' import { Bip44Wallet, Account, SendBlock, Rpc, Tools } from '../dist/main.min.js' -let rpc -//@ts-ignore -var process = process || null -if (process) { - //@ts-expect-error - rpc = new Rpc(process?.env?.NODE_URL ?? '', process?.env?.API_KEY_NAME) -} +const rpc = new Rpc(env?.NODE_URL ?? '', env?.API_KEY_NAME) await suite('unit conversion tests', async () => { -- 2.47.3