"url": "git+https://zoso.dev/libnemo.git"
},
"scripts": {
- "build": "rm -rf dist && tsc && esbuild main.min=dist/main.js global.min=dist/global.js --outdir=dist --target=es2022 --format=esm --platform=node --bundle --minify --sourcemap",
+ "build": "rm -rf dist && tsc && esbuild main.min=dist/main.js global.min=dist/global.js --outdir=dist --target=es2022 --format=esm --platform=browser --bundle --minify --sourcemap",
"test": "npm run build && node --test --env-file .env",
"test:coverage": "npm run test -- --experimental-test-coverage",
"test:coverage:report": "npm run test:coverage -- --test-reporter=lcov --test-reporter-destination=coverage.info && genhtml coverage.info --output-directory test/coverage && rm coverage.info && xdg-open test/coverage/index.html"
* @param {number} index - Index of the account
* @returns {Promise<Account>}
*/
-globalThis.onmessage = async (event) => {
- console.log('ckd')
- console.dir(event)
- console.dir(event.data)
+onmessage = async (event) => {
let result = null
const { type, seed, index } = event.data
switch (type) {
break
}
}
- globalThis.postMessage(result)
+ postMessage(result)
}
/**
// SPDX-FileCopyrightText: 2024 Chris Duncan <chris@zoso.dev>
// SPDX-License-Identifier: GPL-3.0-or-later
-const Worker = globalThis.Worker ?? (await import('node:worker_threads')).Worker
+type Task = {
+ data: object,
+ resolve: Function
+}
class Thread {
+ isAvailable: boolean
worker: Worker
- tasks: any[]
- get isAvailable () { return this.tasks.length === 0 }
constructor (url: string | URL) {
+ this.isAvailable = true
this.worker = new Worker(new URL(url, import.meta.url))
- this.worker.addEventListener('message', (event) => {
- if (this.tasks.length > 0) {
- this.worker.postMessage(this.tasks.shift())
- }
- })
- this.tasks = new Array()
}
}
* @returns {boolean} True if the data was signed by the public key's matching private key
*/
export class Pool {
- #threads: Thread[] = new Array()
+ #tasks: any[]
+ #threads: Thread[]
#url
constructor (url: string | URL) {
+ this.#tasks = []
this.#url = new URL(url, import.meta.url)
- this.#threads = [...Array(navigator.hardwareConcurrency)]
- this.#threads = this.#threads.map(() => { return new Thread(this.#url) })
+ this.#threads = Array(navigator.hardwareConcurrency)
+ .fill(undefined)
+ .map(() => { return new Thread(this.#url) })
}
async work (data: object): Promise<any> {
- const thread = this.#threads.reduce((curr, next) => {
- return (next.tasks.length < curr.tasks.length)
- ? next
- : curr
- })
- thread.tasks.push(data)
- thread.worker.postMessage(thread.tasks.shift())
return new Promise(resolve => {
- thread.worker.addEventListener('message', (event: any) => {
- resolve(event.data)
- }, { once: true })
+ const thread = this.#threads.find(t => t.isAvailable)
+ if (thread) {
+ this.assign(thread, { data, resolve })
+ } else {
+ this.#tasks.push({ data, resolve })
+ }
})
}
+
+ assign (thread: Thread, task: Task) {
+ thread.isAvailable = false
+ thread.worker.addEventListener('message', (event) => {
+ if (this.#tasks.length > 0) {
+ const task = this.#tasks.shift()
+ this.assign(thread, task.data)
+ } else {
+ thread.isAvailable = true
+ }
+ task.resolve(event.data)
+ }, { once: true })
+ thread.worker.postMessage(task.data)
+ }
}
import { ADDRESS_GAP, SEED_LENGTH_BIP44, SEED_LENGTH_BLAKE2B } from './constants.js'\r
import { bytes, dec } from './convert.js'\r
import { Entropy } from './entropy.js'\r
-import { Pool } from './pool.js'\r
+// import { Pool } from './pool.js'\r
import { Rpc } from './rpc.js'\r
import { Safe } from './safe.js'\r
import Tools from './tools.js'\r
import type { Ledger } from './ledger.js'\r
\r
-const ckdPool = new Pool('./ckd.js')\r
+// const ckdPool = new Pool('./ckd.js')\r
\r
/**\r
* Represents a wallet containing numerous Nano accounts derived from a single\r
from = to\r
to = swap\r
}\r
- const accounts: Account[] = []\r
+ const accountQueue = Array(to + 1)\r
for (let i = from; i <= to; i++) {\r
- if (this.#accounts[i]) {\r
- accounts.push(this.#accounts[i])\r
- } else {\r
- const account = await this.ckd(i)\r
- if (account != null) {\r
- this.#accounts[i] = account\r
- accounts.push(account)\r
+ if (this.#accounts[i] == null) {\r
+ accountQueue[i] = this.ckd(i)\r
+ }\r
+ }\r
+ if (accountQueue.length > 0) {\r
+ const results = await Promise.allSettled(accountQueue)\r
+ for (let i = results.length - 1; i >= 0; i--) {\r
+ const result = results[i] as PromiseFulfilledResult<Account>\r
+ if (result?.value != null) {\r
+ this.#accounts[i] = result.value\r
}\r
}\r
}\r
- return accounts\r
+ return this.#accounts.slice(from, to + 1)\r
}\r
\r
/**\r
* @returns {Promise<Account>}\r
*/\r
async ckd (index: number): Promise<Account> {\r
- return await ckdPool.work({ type: 'bip44', seed: this.seed, index })\r
+ const key = await nanoCKD(this.seed, index)\r
+ return await Account.fromPrivateKey(key, index)\r
+ // return await ckdPool.work({ type: 'bip44', seed: this.seed, index })\r
}\r
}\r
\r
* @returns {Promise<Account>}\r
*/\r
async ckd (index: number): Promise<Account> {\r
- return await ckdPool.work({ type: 'blake2b', seed: this.seed, index })\r
+ const hash = await Tools.blake2b([this.seed, dec.toHex(index, 4)])\r
+ const key = bytes.toHex(hash)\r
+ return await Account.fromPrivateKey(key, index)\r
+ // return await ckdPool.work({ type: 'blake2b', seed: this.seed, index })\r
}\r
}\r
\r