]> git.codecow.com Git - libnemo.git/commitdiff
Solidify vault timer implementation.
authorChris Duncan <chris@codecow.com>
Mon, 10 Aug 2026 04:29:27 +0000 (21:29 -0700)
committerChris Duncan <chris@codecow.com>
Mon, 10 Aug 2026 04:29:27 +0000 (21:29 -0700)
src/lib/vault/vault-timer.ts
src/lib/vault/vault-worker.ts

index bbde2b7b4290ab25b919fcf67444110a65f7effd..0bfe634d17b98c703c814848f1b6d5902d7442d7 100644 (file)
@@ -1,6 +1,11 @@
 //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
 //! SPDX-License-Identifier: GPL-3.0-or-later
 
+/**
+ * Executes a function after a timeout, with additional controls to manage the
+ * timer. Note: `this.#ticker` is a no-op every second in order to keep the
+ * timer from being put to sleep by the system.
+ */
 export class VaultTimer {
        #f: () => any
        #isPaused: boolean = false
@@ -27,6 +32,14 @@ export class VaultTimer {
                }
        }
 
+       reset (t: number) {
+               this.stop()
+               this.#remaining = t
+               this.#start = performance.now()
+               this.#ticker = setInterval(Function.prototype, 1000)
+               this.#timeout = setTimeout(this.#f, this.#remaining)
+       }
+
        resume () {
                if (this.#isPaused) {
                        this.#start = performance.now()
@@ -35,4 +48,12 @@ export class VaultTimer {
                        this.#isPaused = false
                }
        }
+
+       stop () {
+               clearTimeout(this.#timeout)
+               clearInterval(this.#ticker)
+               this.#remaining = 0
+               this.#start = 0
+               this.#isPaused = false
+       }
 }
index e326e7a66a82d6684dd1ee4b371ec5317cea183b..1bf5b1915d4fbe89326fcf085b639cd28efb98bc 100644 (file)
@@ -16,7 +16,7 @@ import { VaultTimer } from './vault-timer'
 
 let _locked: boolean = true
 let _timeout: number = 120_000
-let _timer: VaultTimer = new VaultTimer(() => { }, 0)
+let _timer: VaultTimer = new VaultTimer(_autolock, _timeout)
 let _type: 'BIP-44' | 'BLAKE2b' | 'Exodus' | undefined = undefined
 let _id: UUID | undefined = undefined
 let _seed: ArrayBuffer | undefined = undefined
@@ -128,24 +128,25 @@ NODE: parentPort?.on('message', listener)
  */
 function config (timeout?: number): Promise<void> {
        try {
-               _timer?.pause()
+               _timer.pause()
                if (_locked) {
                        throw new VaultError('Wallet is locked')
                }
-               if (typeof timeout === 'number') {
-                       if (timeout < 10) {
-                               throw new VaultError('Timeout must be at least 10 seconds')
-                       }
-                       if (timeout > 600) {
-                               throw new VaultError('Timeout must be at most 10 minutes')
-                       }
-                       _timeout = timeout * 1000
-                       _timer = new VaultTimer(_autolock, _timeout)
+               if (typeof timeout !== 'number') {
+                       throw new TypeError('Invalid timeout period', { cause: timeout })
                }
+               if (timeout < 10) {
+                       throw new VaultError('Timeout must be at least 10 seconds')
+               }
+               if (timeout > 600) {
+                       throw new VaultError('Timeout must be at most 10 minutes')
+               }
+               _timeout = timeout * 1000
+               _timer.reset(_timeout)
                return Promise.resolve()
        } catch (err) {
                console.error(err)
-               _timer?.resume()
+               _timer.resume()
                throw new VaultError('Failed to configure Vault', { cause: err })
        }
 }
@@ -219,7 +220,7 @@ function derive (index?: number | Uint32Array): Promise<Record<string, number |
                                for (const result of results) {
                                        data[result.index] = result.publicKey
                                }
-                               _timer = new VaultTimer(_autolock, _timeout)
+                               _timer.reset(_timeout)
                                return data
                        })
                        .catch(err => {
@@ -299,7 +300,7 @@ function sign (index?: Uint32Array, data?: ArrayBuffer): Promise<Record<string,
                                pub.fill(0)
                                const sig = nano25519_sign(new Uint8Array(data), sk)
                                sk.fill(0)
-                               _timer = new VaultTimer(_autolock, _timeout)
+                               _timer.reset(_timeout)
                                return { signature: sig.buffer }
                        })
                        .catch(err => {
@@ -355,7 +356,7 @@ function unlock (type?: WalletType, id?: UUID, key?: CryptoKey, iv?: ArrayBuffer
                                        _seed = seed
                                        _mnemonic = mnemonic
                                        _locked = false
-                                       _timer = new VaultTimer(_autolock, _timeout)
+                                       _timer.reset(_timeout)
                                        return Promise.resolve({ isLocked: false })
                                })
                                .catch(err => {
@@ -389,7 +390,7 @@ function update (key?: CryptoKey, salt?: ArrayBuffer): Promise<Record<string, Ar
                }
                return WalletAesGcm.encrypt(_type, _id, key, _seed, _mnemonic)
                        .then(({ iv, encrypted }) => {
-                               _timer = new VaultTimer(_autolock, _timeout)
+                               _timer.reset(_timeout)
                                return { iv, salt, encrypted }
                        })
                        .catch(err => {