]> zoso.dev Git - libnemo.git/commitdiff
Reintroduce former Pool as Thread so that ckd tasks can be managed in a queue.
authorChris Duncan <chris@zoso.dev>
Sun, 17 Nov 2024 07:21:04 +0000 (23:21 -0800)
committerChris Duncan <chris@zoso.dev>
Sun, 17 Nov 2024 07:21:04 +0000 (23:21 -0800)
src/lib/thread.ts [new file with mode: 0644]
src/lib/wallet.ts

diff --git a/src/lib/thread.ts b/src/lib/thread.ts
new file mode 100644 (file)
index 0000000..79addc4
--- /dev/null
@@ -0,0 +1,59 @@
+// SPDX-FileCopyrightText: 2024 Chris Duncan <chris@zoso.dev>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+if (globalThis.Worker == null) {
+  const { Worker } = await import('node:worker_threads')
+  //@ts-expect-error
+  Worker.prototype.addEventListener = Worker.prototype.addListener
+  //@ts-expect-error
+  globalThis.Worker = Worker
+}
+
+type Task = {
+  data: object,
+  resolve: Function
+}
+
+/**
+* Processes tasks from a queue using a Web Worker.
+*/
+export class Thread {
+  #isAvailable: boolean = true
+  #queue: Task[] = []
+  #task?: Task
+  #worker: Worker
+
+  #post (next: Task) {
+    this.#isAvailable = false
+    this.#task = next
+    this.#worker.postMessage(next.data)
+  }
+
+  constructor (url: string | URL) {
+    this.#worker = new Worker(new URL(url, import.meta.url), { type: 'module' })
+    this.#worker.addEventListener('message', (event) => {
+      const result = event.data ?? event
+      if (this.#task == null) {
+        throw new ReferenceError(`Error resolving Worker result: ${result}`)
+      }
+      const resolve = this.#task.resolve
+      const next = this.#queue.shift()
+      if (next == null) {
+        this.#isAvailable = true
+      } else {
+        this.#post(next)
+      }
+      resolve(result)
+    })
+  }
+
+  async work (data: object): Promise<any> {
+    return new Promise(resolve => {
+      if (this.#isAvailable) {
+        this.#post({ data, resolve })
+      } else {
+        this.#queue.push({ data, resolve })
+      }
+    })
+  }
+}
index 8d8fa80e6785e6aa0034d99987c26a9aacb10b8b..1a4f5cc51be348e1258779e40642e03f3e00161c 100644 (file)
@@ -5,6 +5,7 @@ import { Account } from './account.js'
 import { Bip39Mnemonic } from './bip39-mnemonic.js'\r
 import { ADDRESS_GAP, SEED_LENGTH_BIP44, SEED_LENGTH_BLAKE2B } from './constants.js'\r
 import { Entropy } from './entropy.js'\r
+import { Thread } from './thread.js'\r
 import { Rpc } from './rpc.js'\r
 import { Safe } from './safe.js'\r
 import type { Ledger } from './ledger.js'\r
@@ -230,14 +231,14 @@ abstract class Wallet {
 */\r
 export class Bip44Wallet extends Wallet {\r
        static #isInternal: boolean = false\r
-       #worker: Worker\r
+       #thread: Thread\r
 \r
        constructor (seed: string, mnemonic?: Bip39Mnemonic, id?: string) {\r
                if (!Bip44Wallet.#isInternal) {\r
                        throw new Error(`Bip44Wallet cannot be instantiated directly. Use 'await Bip44Wallet.create()' instead.`)\r
                }\r
                super(seed, mnemonic, id)\r
-               this.#worker = new Worker(new URL('./ckd.js', import.meta.url), { type: 'module' })\r
+               this.#thread = new Thread(new URL('./ckd.js', import.meta.url))\r
                Bip44Wallet.#isInternal = false\r
        }\r
 \r
@@ -389,16 +390,11 @@ export class Bip44Wallet extends Wallet {
        * @returns {Promise<Account>}\r
        */\r
        async ckd (index: number): Promise<Account> {\r
-               return new Promise(resolve => {\r
-                       this.#worker.addEventListener('message', (message) => {\r
-                               const key = message.data ?? message\r
-                               if (typeof key !== 'string') {\r
-                                       throw new TypeError('BIP-44 child key derivation returned invalid data')\r
-                               }\r
-                               Account.fromPrivateKey(key, index).then(resolve)\r
-                       }, { once: true })\r
-                       this.#worker.postMessage({ type: 'bip44', seed: this.seed, index })\r
-               })\r
+               const key = await this.#thread.work({ type: 'bip44', seed: this.seed, index })\r
+               if (typeof key !== 'string') {\r
+                       throw new TypeError('BIP-44 child key derivation returned invalid data')\r
+               }\r
+               return Account.fromPrivateKey(key, index)\r
        }\r
 }\r
 \r
@@ -420,14 +416,14 @@ export class Bip44Wallet extends Wallet {
 */\r
 export class Blake2bWallet extends Wallet {\r
        static #isInternal: boolean = false\r
-       #worker: Worker\r
+       #thread: Thread\r
 \r
        constructor (seed: string, mnemonic?: Bip39Mnemonic, id?: string) {\r
                if (!Blake2bWallet.#isInternal) {\r
                        throw new Error(`Blake2bWallet cannot be instantiated directly. Use 'await Blake2bWallet.create()' instead.`)\r
                }\r
                super(seed, mnemonic, id)\r
-               this.#worker = new Worker(new URL('./ckd.js', import.meta.url), { type: 'module' })\r
+               this.#thread = new Thread(new URL('./ckd.js', import.meta.url))\r
                Blake2bWallet.#isInternal = false\r
        }\r
 \r
@@ -541,16 +537,11 @@ export class Blake2bWallet extends Wallet {
        * @returns {Promise<Account>}\r
        */\r
        async ckd (index: number): Promise<Account> {\r
-               return new Promise(resolve => {\r
-                       this.#worker.addEventListener('message', (message) => {\r
-                               const key = message.data ?? message\r
-                               if (typeof key !== 'string') {\r
-                                       throw new TypeError('BLAKE2b child key derivation returned invalid data')\r
-                               }\r
-                               Account.fromPrivateKey(key, index).then(resolve)\r
-                       }, { once: true })\r
-                       this.#worker.postMessage({ type: 'blake2b', seed: this.seed, index })\r
-               })\r
+               const key = await this.#thread.work({ type: 'blake2b', seed: this.seed, index })\r
+               if (typeof key !== 'string') {\r
+                       throw new TypeError('BLAKE2b child key derivation returned invalid data')\r
+               }\r
+               return Account.fromPrivateKey(key, index)\r
        }\r
 }\r
 \r