]> git.codecow.com Git - libnemo.git/commitdiff
Begin refactoring to allow Accounts to be encrypted en masse while retaining their...
authorChris Duncan <chris@zoso.dev>
Fri, 18 Jul 2025 06:05:03 +0000 (23:05 -0700)
committerChris Duncan <chris@zoso.dev>
Fri, 18 Jul 2025 06:05:03 +0000 (23:05 -0700)
src/lib/account.ts
src/lib/bip39-mnemonic.ts
src/lib/block.ts
src/lib/tools.ts
src/lib/wallets/bip44-wallet.ts
src/lib/wallets/blake2b-wallet.ts
src/lib/wallets/wallet.ts
src/lib/workers/nano-nacl.ts
src/lib/workers/queue.ts
src/lib/workers/safe.ts
src/types.d.ts

index f7cf1100e304245122164136ead5d7a73843c3c9..3044ea15893fd1088eb16ea9c18dded71291dcbc 100644 (file)
@@ -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'\r
 import { base32, bytes, hex, utf8 } from './convert'\r
 import { Rpc } from './rpc'\r
-import { Data } from '#types'\r
+import { Data, Key, KeyPair } from '#types'\r
 import { NanoNaClWorker, SafeWorker } from '#workers'\r
 \r
 /**\r
@@ -19,6 +19,7 @@ export class Account {
        static #isInternal: boolean = false\r
 \r
        #address: string\r
+       #index?: number\r
        #publicKey: Uint8Array<ArrayBuffer>\r
 \r
        #balance?: bigint\r
@@ -28,6 +29,7 @@ export class Account {
        #weight?: bigint\r
 \r
        get address () { return `${PREFIX}${this.#address}` }\r
+       get index () { return this.#index }\r
        get publicKey () { return bytes.toHex(this.#publicKey) }\r
 \r
        get balance () { return this.#balance }\r
@@ -50,7 +52,7 @@ export class Account {
        }\r
        set weight (v) { this.#weight = v ? BigInt(v) : undefined }\r
 \r
-       private constructor (address: string, publicKey: Uint8Array<ArrayBuffer>) {\r
+       private constructor (address: string, publicKey: Uint8Array<ArrayBuffer>, index?: number) {\r
                if (!Account.#isInternal) {\r
                        throw new Error(`Account cannot be instantiated directly. Use factory methods instead.`)\r
                }\r
@@ -58,6 +60,7 @@ export class Account {
                        .replace(PREFIX, '')\r
                        .replace(PREFIX_LEGACY, '')\r
                this.#publicKey = publicKey\r
+               this.#index = index\r
        }\r
 \r
        /**\r
@@ -128,44 +131,24 @@ export class Account {
        * and stored in IndexedDB. The corresponding public key will automatically be\r
        * derived and saved.\r
        *\r
-       * @param {string} privateKey - Private key of the account\r
-       * @param {(string|Uint8Array)} password - Used to encrypt the private key\r
+       * @param {KeyPair} keypair - Index and keys of the account\r
+       * @param {Key} password - Used to encrypt the private key\r
        * @returns {Account} A new Account object\r
        */\r
-       static async import (privateKey: string, password: string | Uint8Array<ArrayBuffer>): Promise<Account>\r
-       /**\r
-       * Instantiates an Account object from its private key which is then encrypted\r
-       * and stored in IndexedDB. The corresponding public key will automatically be\r
-       * derived and saved.\r
-       *\r
-       * @param {Uint8Array} privateKey - Private key of the account\r
-       * @param {(string|Uint8Array)} password - Used to encrypt the private key\r
-       * @returns {Account} A new Account object\r
-       */\r
-       static async import (privateKey: Uint8Array<ArrayBuffer>, password: string | Uint8Array<ArrayBuffer>): Promise<Account>\r
-       /**\r
-       * Instantiates Account objects from their private keys which are then\r
-       * encrypted and stored in IndexedDB. The corresponding public keys will\r
-       * automatically be derived and saved.\r
-       *\r
-       * @param {string[]} privateKeys - Private keys of the account\r
-       * @param {(string|Uint8Array)} password - Used to encrypt the private keys\r
-       * @returns {Account[]} The instantiated Account objects\r
-       */\r
-       static async import (privateKeys: string[], password: string | Uint8Array<ArrayBuffer>): Promise<Account[]>\r
+       static async import (keypair: KeyPair, password: Key): Promise<Account>\r
        /**\r
        * Instantiates Account objects from their private keys which are then\r
        * encrypted and stored in IndexedDB. The corresponding public keys will\r
        * automatically be derived and saved.\r
        *\r
-       * @param {Uint8Array[]} privateKeys - Private keys of the account\r
-       * @param {(string|Uint8Array)} password - Used to encrypt the private keys\r
+       * @param {KeyPair[]} keypairs - Indexes and keys of the accounts\r
+       * @param {Key} password - Used to encrypt the private keys\r
        * @returns {Account[]} The instantiated Account objects\r
        */\r
-       static async import (privateKeys: Uint8Array<ArrayBuffer>[], password: string | Uint8Array<ArrayBuffer>): Promise<Account[]>\r
-       static import (input: string | string[] | Uint8Array<ArrayBuffer> | Uint8Array<ArrayBuffer>[], password?: string | Uint8Array<ArrayBuffer>): Account | Account[] | Promise<Account | Account[]> {\r
+       static async import (keypairs: KeyPair[], password: Key): Promise<Account[]>\r
+       static import (input: Key | Key[] | KeyPair | KeyPair[], password?: Key): Account | Account[] | Promise<Account | Account[]> {\r
                if (Array.isArray(input)) {\r
-                       if (password != null) {\r
+                       if (this.#isKeyPairs(input) && password != null) {\r
                                return new Promise((resolve, reject): void => {\r
                                        this.#fromPrivate(input, password)\r
                                                .then(r => resolve(r))\r
@@ -174,14 +157,15 @@ export class Account {
                        }\r
                        return this.#fromPublic(input)\r
                } else {\r
-                       if (password != null) {\r
+                       const inputs = [input]\r
+                       if (this.#isKeyPairs(inputs) && password != null) {\r
                                return new Promise((resolve, reject): void => {\r
-                                       this.#fromPrivate([input] as string[], password)\r
+                                       this.#fromPrivate(inputs, password)\r
                                                .then(r => resolve(r[0]))\r
                                                .catch(e => reject(e))\r
                                })\r
                        }\r
-                       return this.#fromPublic(input)[0]\r
+                       return this.#fromPublic(inputs)[0]\r
                }\r
        }\r
 \r
@@ -221,11 +205,11 @@ export class Account {
        * Signs a block using the private key of the account. The signature is\r
        * appended to the signature field of the block before being returned.\r
        *\r
-       * @param {(string|Uint8Array)} password - Required to decrypt the private key for signing\r
+       * @param {Key} password - Required to decrypt the private key for signing\r
        * @param {(ChangeBlock|ReceiveBlock|SendBlock)} block - The block data to be hashed and signed\r
        * @returns {Promise<string>} Hexadecimal-formatted 64-byte signature\r
        */\r
-       async sign (block: ChangeBlock | ReceiveBlock | SendBlock, password: string | Uint8Array<ArrayBuffer>): Promise<string> {\r
+       async sign (block: ChangeBlock | ReceiveBlock | SendBlock, password: Key): Promise<string> {\r
                const privateKey = await this.exportPrivateKey(password)\r
                try {\r
                        const headers = {\r
@@ -249,19 +233,19 @@ export class Account {
        * Retrieves and decryptes the private key of the Account. The same password\r
        * used to lock it must be used to unlock it.\r
        *\r
-       * @param {(string|Uint8Array)} password Used previously to lock the Account\r
+       * @param {Key} password Used previously to lock the Account\r
        * @returns Private key bytes as a Uint8Array\r
        */\r
-       async exportPrivateKey (password: string | Uint8Array<ArrayBuffer>): Promise<Uint8Array<ArrayBuffer>>\r
+       async exportPrivateKey (password: Key): Promise<Uint8Array<ArrayBuffer>>\r
        /**\r
        * Retrieves and decryptes the private key of the Account. The same password\r
        * used to lock it must be used to unlock it.\r
        *\r
-       * @param {(string|Uint8Array)} password Used previously to lock the Account\r
+       * @param {Key} password Used previously to lock the Account\r
        * @returns Private key bytes as a hexadecimal string\r
        */\r
-       async exportPrivateKey (password: string | Uint8Array<ArrayBuffer>, format: 'hex'): Promise<string>\r
-       async exportPrivateKey (password: string | Uint8Array<ArrayBuffer>, format?: 'hex'): Promise<string | Uint8Array<ArrayBuffer>> {\r
+       async exportPrivateKey (password: Key, format: 'hex'): Promise<string>\r
+       async exportPrivateKey (password: Key, format?: 'hex'): Promise<Key> {\r
                if (typeof password === 'string') password = utf8.toBytes(password)\r
                if (password == null || !(password instanceof Uint8Array)) {\r
                        throw new Error('Password must be string or bytes')\r
@@ -270,10 +254,12 @@ export class Account {
                        const headers = {\r
                                method: 'get',\r
                                name: this.publicKey,\r
-                               store: 'Account',\r
+                               store: 'Account'\r
+                       }\r
+                       const data = {\r
                                password: password.buffer\r
                        }\r
-                       const response = await SafeWorker.assign(headers)\r
+                       const response = await SafeWorker.assign(headers, data)\r
                        const privateKey = new Uint8Array(response[this.publicKey] as ArrayBuffer)\r
                        return format === 'hex'\r
                                ? bytes.toHex(privateKey)\r
@@ -337,43 +323,48 @@ export class Account {
        * and stored in IndexedDB. The corresponding public key will automatically be\r
        * derived and saved.\r
        *\r
-       * @param {(string|Uint8Array)} privateKeys - Private key of the account\r
+       * @param {KeyPair} keypairs - Indexes and keys of the accounts\r
        * @param {number} [index] - Account number used when deriving the key\r
        * @returns {Account} A new Account object\r
        */\r
-       static async #fromPrivate (privateKeys: string[] | Uint8Array<ArrayBuffer>[], password: string | Uint8Array<ArrayBuffer>): Promise<Account[]> {\r
+       static async #fromPrivate (keypairs: KeyPair[], password: Key): Promise<Account[]> {\r
                if (typeof password === 'string') password = utf8.toBytes(password)\r
                if (password == null || !(password instanceof Uint8Array)) {\r
                        throw new Error('Invalid password when importing Account')\r
                }\r
 \r
-               const keypairs: Data = {}\r
-               for (let privateKey of privateKeys) {\r
+               const accounts: Account[] = []\r
+               const data: Data = {}\r
+               for (let keypair of keypairs) {\r
+                       let { index, privateKey } = keypair\r
+                       if (index == null) {\r
+                               throw new RangeError('Index missing for Account')\r
+                       }\r
                        this.#validateKey(privateKey)\r
                        if (typeof privateKey === 'string') privateKey = hex.toBytes(privateKey)\r
-                       let publicKey: string\r
                        try {\r
                                const headers = {\r
-                                       method: 'convert'\r
+                                       method: 'convert',\r
+                                       privateKey: privateKey.buffer\r
                                }\r
-                               const data = {\r
-                                       privateKey: new Uint8Array(privateKey).buffer\r
-                               }\r
-                               publicKey = await NanoNaClWorker.assign(headers, data)\r
-                               keypairs[publicKey] = privateKey.buffer\r
+                               const publicKey = await NanoNaClWorker.assign(headers)\r
+                               data[publicKey] = privateKey.buffer\r
+\r
+                               const address = this.#keyToAddress(publicKey)\r
+                               this.#isInternal = true\r
+                               accounts.push(new this(address, publicKey, index))\r
                        } catch (err) {\r
                                throw new Error(`Failed to derive public key from private key`, { cause: err })\r
                        }\r
                }\r
 \r
-               const accounts = await this.import(Object.keys(keypairs))\r
                try {\r
                        const headers = {\r
                                method: 'set',\r
-                               store: 'Account',\r
-                               password: password.buffer\r
+                               store: 'Account'\r
                        }\r
-                       const isLocked = await SafeWorker.assign(headers, keypairs)\r
+                       data.password = password.buffer\r
+                       const isLocked = await SafeWorker.assign(headers, data)\r
                        if (!isLocked) {\r
                                throw null\r
                        }\r
@@ -392,12 +383,14 @@ export class Account {
        * @param {(string[]|Uint8Array[])} input - Public keys or addresses of the accounts\r
        * @returns {Account[]} The instantiated Account objects\r
        */\r
-       static #fromPublic (input: string[] | Uint8Array<ArrayBuffer>[] | unknown): Account[] {\r
-               const inputArray: unknown[] = Array.isArray(input) ? input : [input]\r
+       static #fromPublic (input: KeyPair[] | unknown): Account[] {\r
+               if (!this.#isKeys(input)) {\r
+                       throw new TypeError('Invalid public input for Account')\r
+               }\r
                const accounts: Account[] = []\r
                let address: string\r
                let publicKey: Uint8Array<ArrayBuffer>\r
-               for (let i of inputArray) {\r
+               for (let i of input) {\r
                        let keyError, addressError\r
                        try {\r
                                this.#validateKey(i)\r
@@ -422,6 +415,32 @@ export class Account {
                return accounts\r
        }\r
 \r
+       static #isKeys (input: unknown): input is Key[] {\r
+               if (Array.isArray(input)) {\r
+                       for (const i of input) {\r
+                               if (typeof i !== 'string' && !(i instanceof Uint8Array && 'buffer' in i)) {\r
+                                       return false\r
+                               }\r
+                       }\r
+               }\r
+               return true\r
+       }\r
+\r
+       static #isKeyPairs (input: unknown): input is KeyPair[] {\r
+               if (Array.isArray(input)) {\r
+                       for (const i of input) {\r
+                               if (typeof input !== 'object') {\r
+                                       return false\r
+                               }\r
+                               const obj = i as { [key: string]: unknown }\r
+                               if ('index' in obj || 'privateKey' in obj || 'publicKey' in obj) {\r
+                                       return true\r
+                               }\r
+                       }\r
+               }\r
+               return false\r
+       }\r
+\r
        /**\r
        * Converts a public key to a Nano address.\r
        *\r
@@ -441,7 +460,7 @@ export class Account {
        * @param {unknown} key - Key bytes as Uint8Array or hexadecimal string\r
        * @throws If key is invalid\r
        */\r
-       static #validateKey (key: unknown): asserts key is (string | Uint8Array<ArrayBuffer>) {\r
+       static #validateKey (key: unknown): asserts key is (Key) {\r
                if (key === undefined) {\r
                        throw new TypeError(`Key is undefined`)\r
                }\r
index e286d766c2a79a33fd71e702462c8cf0eb764dd4..9476941cea26e985af91810f69a7ee8df93b2275 100644 (file)
@@ -5,8 +5,7 @@ import { Bip39Words } from './bip39-wordlist'
 import { BIP39_ITERATIONS } from './constants'\r
 import { bin, bytes, dec, utf8 } from './convert'\r
 import { Entropy } from './entropy'\r
-\r
-const { subtle } = globalThis.crypto\r
+import { Key } from '#types'\r
 \r
 /**\r
 * Represents a mnemonic phrase that identifies a wallet as defined by BIP-39.\r
@@ -78,7 +77,7 @@ export class Bip39Mnemonic {
         * @returns {Promise<string>} First N/32 bits of the hash as a hexadecimal string\r
         */\r
        static async checksum (entropy: Entropy): Promise<string> {\r
-               const hashBuffer = await subtle.digest('SHA-256', entropy.bytes)\r
+               const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', entropy.bytes)\r
                const hashBytes = new Uint8Array(hashBuffer)\r
                const hashBits = bytes.toBin(hashBytes)\r
                const checksumLength = entropy.bits.length / 32\r
@@ -146,7 +145,7 @@ export class Bip39Mnemonic {
                                passphrase = ''\r
                        }\r
                        const keyData = utf8.toBytes(this.phrase)\r
-                       const phraseKey = await subtle.importKey('raw', keyData, 'PBKDF2', false, ['deriveBits', 'deriveKey'])\r
+                       const phraseKey = await globalThis.crypto.subtle.importKey('raw', keyData, 'PBKDF2', false, ['deriveBits', 'deriveKey'])\r
                        const derivedKeyType: HmacImportParams = {\r
                                name: 'HMAC',\r
                                hash: 'SHA-512',\r
@@ -160,8 +159,8 @@ export class Bip39Mnemonic {
                                salt: utf8.toBytes(passphrase),\r
                                iterations: BIP39_ITERATIONS\r
                        }\r
-                       const seedKey = await subtle.deriveKey(algorithm, phraseKey, derivedKeyType, true, ['sign'])\r
-                       const seedBuffer = await subtle.exportKey('raw', seedKey)\r
+                       const seedKey = await globalThis.crypto.subtle.deriveKey(algorithm, phraseKey, derivedKeyType, true, ['sign'])\r
+                       const seedBuffer = await globalThis.crypto.subtle.exportKey('raw', seedKey)\r
                        this.#bip44Seed = new Uint8Array(seedBuffer)\r
                }\r
                return format === 'hex'\r
@@ -176,7 +175,7 @@ export class Bip39Mnemonic {
        * @returns {string} Hexadecimal seed\r
        */\r
        async toBlake2bSeed (format: 'hex'): Promise<string>\r
-       async toBlake2bSeed (format?: 'hex'): Promise<string | Uint8Array<ArrayBuffer>> {\r
+       async toBlake2bSeed (format?: 'hex'): Promise<Key> {\r
                if (this.#blake2bSeed == null) {\r
                        const wordArray = this.phrase.split(' ')\r
                        const bits = wordArray.map((w: string) => {\r
index ebd164cfc2be9729d86b5b2b2b99d769ec89f70a..57e032b11bea1eca3bf686bd909ba85dbdf4dcf3 100644 (file)
@@ -141,7 +141,7 @@ abstract class Block {
                } else {
                        try {
                                const account = (typeof input === 'string')
-                                       ? await Account.import(input, '')
+                                       ? await Account.import({ privateKey: input }, '')
                                        : this.account
                                this.signature = await account.sign(this, '')
                        } catch (err) {
index 84a53284ea704e07cb8eb9ffedf83ec7a100f8a3..a9a445910988789ff4a5f456947f0553224983a7 100644 (file)
@@ -8,6 +8,7 @@ import { UNITS } from './constants'
 import { bytes, hex } from './convert'
 import { Rpc } from './rpc'
 import { Bip44Wallet, Blake2bWallet, LedgerWallet } from './wallets'
+import { Key } from '#types'
 import { NanoNaClWorker } from '#workers'
 
 type SweepResult = {
@@ -83,11 +84,11 @@ export async function convert (amount: bigint | string, inputUnit: string, outpu
 /**
 * Signs arbitrary strings with a private key using the Ed25519 signature scheme.
 *
-* @param {(string|Uint8Array)} key - Hexadecimal-formatted private key to use for signing
+* @param {Key} key - Hexadecimal-formatted private key to use for signing
 * @param {...string} input - Data to be signed
 * @returns {Promise<string>} Hexadecimal-formatted signature
 */
-export async function sign (key: string | Uint8Array<ArrayBuffer>, ...input: string[]): Promise<string> {
+export async function sign (key: Key, ...input: string[]): Promise<string> {
        if (typeof key === 'string') key = hex.toBytes(key)
        let signature: string
        try {
@@ -170,12 +171,12 @@ export async function sweep (
 /**
 * Verifies the signature of arbitrary strings using a public key.
 *
-* @param {(string|Uint8Array)} key - Hexadecimal-formatted public key to use for verification
+* @param {Key} key - Hexadecimal-formatted public key to use for verification
 * @param {string} signature - Hexadcimal-formatted signature
 * @param {...string} input - Data to be verified
 * @returns {Promise<boolean>} True if the data was signed by the public key's matching private key
 */
-export async function verify (key: string | Uint8Array<ArrayBuffer>, signature: string, ...input: string[]): Promise<boolean> {
+export async function verify (key: Key, signature: string, ...input: string[]): Promise<boolean> {
        if (typeof key === 'string') key = hex.toBytes(key)
        try {
                const headers = {
index dd01f2d3bef4190d7a2e5d14fbd1ce32d1cc8edc..6cbe42470f92219d0ce39ca0a27ce49a939e1487 100644 (file)
@@ -6,7 +6,7 @@ import { Bip39Mnemonic } from '#src/lib/bip39-mnemonic.js'
 import { SEED_LENGTH_BIP44 } from '#src/lib/constants.js'\r
 import { bytes, hex, utf8 } from '#src/lib/convert.js'\r
 import { Entropy } from '#src/lib/entropy.js'\r
-import { KeyPair } from '#types'\r
+import { Key, KeyPair } from '#types'\r
 import { Bip44CkdWorker } from '#workers'\r
 \r
 /**\r
@@ -89,7 +89,7 @@ export class Bip44Wallet extends Wallet {
        * @returns {Bip44Wallet} A newly instantiated Bip44Wallet\r
        */\r
        static async fromEntropy (key: Uint8Array<ArrayBuffer>, entropy: string, salt?: string): Promise<Bip44Wallet>\r
-       static async fromEntropy (passkey: string | Uint8Array<ArrayBuffer>, entropy: string, salt: string = ''): Promise<Bip44Wallet> {\r
+       static async fromEntropy (passkey: Key, entropy: string, salt: string = ''): Promise<Bip44Wallet> {\r
                if (typeof passkey === 'string') passkey = utf8.toBytes(passkey)\r
                let wallet: Bip44Wallet\r
                try {\r
@@ -129,7 +129,7 @@ export class Bip44Wallet extends Wallet {
        * @returns {Bip44Wallet} A newly instantiated Bip44Wallet\r
        */\r
        static async fromMnemonic (key: Uint8Array<ArrayBuffer>, mnemonic: string, salt?: string): Promise<Bip44Wallet>\r
-       static async fromMnemonic (passkey: string | Uint8Array<ArrayBuffer>, mnemonic: string, salt: string = ''): Promise<Bip44Wallet> {\r
+       static async fromMnemonic (passkey: Key, mnemonic: string, salt: string = ''): Promise<Bip44Wallet> {\r
                if (typeof passkey === 'string') passkey = utf8.toBytes(passkey)\r
                let wallet: Bip44Wallet\r
                try {\r
@@ -170,7 +170,7 @@ export class Bip44Wallet extends Wallet {
        * @returns {Bip44Wallet} A newly instantiated Bip44Wallet\r
        */\r
        static async fromSeed (key: Uint8Array<ArrayBuffer>, seed: string): Promise<Bip44Wallet>\r
-       static async fromSeed (passkey: string | Uint8Array<ArrayBuffer>, seed: string): Promise<Bip44Wallet> {\r
+       static async fromSeed (passkey: Key, seed: string): Promise<Bip44Wallet> {\r
                if (typeof passkey === 'string') passkey = utf8.toBytes(passkey)\r
                if (seed.length !== SEED_LENGTH_BIP44) {\r
                        throw new Error(`Expected a ${SEED_LENGTH_BIP44}-character seed, but received ${seed.length}-character string.`)\r
index e093069d412d2def6c1903417d39ccc17af718a7..2b9264f9ccd2d7828b7caad8a44f5903f1c1384c 100644 (file)
@@ -7,7 +7,7 @@ import { Blake2b } from '#src/lib/blake2b.js'
 import { SEED_LENGTH_BLAKE2B } from '#src/lib/constants.js'\r
 import { hex, utf8 } from '#src/lib/convert.js'\r
 import { Entropy } from '#src/lib/entropy.js'\r
-import { KeyPair } from '#types'\r
+import { Key, KeyPair } from '#types'\r
 \r
 /**\r
 * BLAKE2b wallet created by deriving a mnemonic phrase from a seed or vice\r
@@ -79,7 +79,7 @@ export class Blake2bWallet extends Wallet {
        * @returns {Blake2bWallet} A newly instantiated Blake2bWallet\r
        */\r
        static async fromSeed (key: Uint8Array<ArrayBuffer>, seed: string): Promise<Blake2bWallet>\r
-       static async fromSeed (passkey: string | Uint8Array<ArrayBuffer>, seed: string): Promise<Blake2bWallet> {\r
+       static async fromSeed (passkey: Key, seed: string): Promise<Blake2bWallet> {\r
                if (typeof passkey === 'string') passkey = utf8.toBytes(passkey)\r
                if (seed.length !== SEED_LENGTH_BLAKE2B) {\r
                        throw new Error(`Expected a ${SEED_LENGTH_BLAKE2B}-character seed, but received ${seed.length}-character string.`)\r
@@ -118,7 +118,7 @@ export class Blake2bWallet extends Wallet {
        * @returns {Blake2bWallet} A newly instantiated Blake2bWallet\r
        */\r
        static async fromMnemonic (key: Uint8Array<ArrayBuffer>, mnemonic: string): Promise<Blake2bWallet>\r
-       static async fromMnemonic (passkey: string | Uint8Array<ArrayBuffer>, mnemonic: string): Promise<Blake2bWallet> {\r
+       static async fromMnemonic (passkey: Key, mnemonic: string): Promise<Blake2bWallet> {\r
                if (typeof passkey === 'string') passkey = utf8.toBytes(passkey)\r
                let wallet: Blake2bWallet\r
                try {\r
index 2aa4d73cfc483b0bf2aa431a331693d08ac42e7e..166ede5f5381eb062ab1a261c760be7b5479c2b3 100644 (file)
@@ -7,7 +7,7 @@ import { ADDRESS_GAP } from '#src/lib/constants.js'
 import { bytes, hex, utf8 } from '#src/lib/convert.js'\r
 import { Entropy } from '#src/lib/entropy.js'\r
 import { Rpc } from '#src/lib/rpc.js'\r
-import { Data, KeyPair } from '#types'\r
+import { Data, Key, KeyPair } from '#types'\r
 import { SafeWorker } from '#workers'\r
 \r
 /**\r
@@ -102,20 +102,28 @@ export abstract class Wallet {
                }\r
                if (indexes.length > 0) {\r
                        const keypairs = await this.ckd(indexes)\r
+                       const privateKeys = []\r
                        for (const keypair of keypairs) {\r
                                const { index, privateKey, publicKey } = keypair\r
                                if (index == null) {\r
                                        throw new RangeError('Account keys derived but index missing')\r
                                }\r
                                if (privateKey != null) {\r
-                                       output[index] = await Account.import(privateKey, this.seed)\r
-                               } else if (publicKey != null) {\r
-                                       output[index] = await Account.import(publicKey)\r
-                               } else {\r
-                                       throw new RangeError('Account keys missing')\r
+                                       privateKeys.push(keypair)\r
+                               } else if (typeof publicKey === 'string') {\r
+                                       output[index] = Account.import(publicKey)\r
+                               } else if (publicKey instanceof Uint8Array) {\r
+                                       output[index] = Account.import(publicKey)\r
                                }\r
                                this.#accounts[index] = output[index]\r
                        }\r
+                       const privateAccounts = await Account.import(privateKeys, this.seed)\r
+                       for (const a of privateAccounts) {\r
+                               if (a.index == null) {\r
+                                       throw new RangeError('Index missing for Account')\r
+                               }\r
+                               output[a.index] = this.#accounts[a.index] = a\r
+                       }\r
                }\r
                return output\r
        }\r
@@ -144,10 +152,10 @@ export abstract class Wallet {
        * Locks the wallet and all currently derived accounts with a password that\r
        * will be needed to unlock it later.\r
        *\r
-       * @param {(string|Uint8Array)} password Used to lock the wallet\r
+       * @param {Key} password Used to lock the wallet\r
        * @returns True if successfully locked\r
        */\r
-       async lock (password: string | Uint8Array<ArrayBuffer>): Promise<boolean> {\r
+       async lock (password: Key): Promise<boolean> {\r
                if (typeof password === 'string') {\r
                        password = utf8.toBytes(password)\r
                }\r
@@ -214,10 +222,10 @@ export abstract class Wallet {
        /**\r
        * Unlocks the wallet using the same password as used prior to lock it.\r
        *\r
-       * @param {(string|Uint8Array)} password Used previously to lock the wallet\r
+       * @param {Key} password Used previously to lock the wallet\r
        * @returns True if successfully unlocked\r
        */\r
-       async unlock (password: string | Uint8Array<ArrayBuffer>): Promise<boolean> {\r
+       async unlock (password: Key): Promise<boolean> {\r
                if (typeof password === 'string') {\r
                        password = utf8.toBytes(password)\r
                }\r
index 2ab492047828ae8057d3842f0e2f77f841ff84e8..f2f9fbc08a2f2a42c5fbd137532e2ea3e419559e 100644 (file)
@@ -6,7 +6,7 @@
 import { WorkerInterface } from './worker-interface'\r
 import { Blake2b } from '#src/lib/blake2b.js'\r
 import { default as Convert, bytes, hex } from '#src/lib/convert.js'\r
-import { Data, Headers } from '#types'\r
+import { Data, Headers, Key } from '#types'\r
 \r
 /**\r
 * Ported in 2014 by Dmitry Chestnykh and Devi Mandiri.\r
@@ -565,10 +565,10 @@ export class NanoNaCl extends WorkerInterface {
        /**\r
        * Derives a public key from a private key.\r
        *\r
-       * @param {(string|Uint8Array)} seed - 32-byte private key\r
+       * @param {Key} seed - 32-byte private key\r
        * @returns 32-byte public key byte array\r
        */\r
-       static convert (seed: string | Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer> {\r
+       static convert (seed: Key): Uint8Array<ArrayBuffer> {\r
                if (typeof seed === 'string') seed = hex.toBytes(seed)\r
                this.checkArrayTypes(seed)\r
                if (seed.length !== this.crypto_sign_SEEDBYTES) {\r
index 3c40cc33745e1526ee768fd17d73dd265c708ecb..59f5f426f8708113b22ec7704cf7cf6f348d55ce 100644 (file)
@@ -78,13 +78,6 @@ export class Queue {
                        const { id, headers, data, reject } = this.#job
                        try {
                                const buffers: ArrayBuffer[] = []
-                               if (headers != null) {
-                                       for (let h of Object.keys(headers)) {
-                                               if (headers[h] instanceof ArrayBuffer) {
-                                                       buffers.push(headers[h])
-                                               }
-                                       }
-                               }
                                if (data != null) {
                                        for (let d of Object.keys(data)) {
                                                buffers.push(data[d])
index bcc8c63f6ef6422317cf36d9fa822a3051bdece3..44ea2f464adcfb516ca9ba280af9daf16a19ce6b 100644 (file)
@@ -22,7 +22,9 @@ export class Safe extends WorkerInterface {
        }
 
        static async work (headers: Headers, data: Data): Promise<any> {
-               const { method, name, store, password } = headers
+               const { method, name, store } = headers
+               const { password } = data
+               delete data.password
                this.#storage = await this.#open(this.DB_NAME)
                let result
                try {
index c868e7f0e1defdd08d1a86adae2265b10d72bc10..bc24af2f320990d20aa2bd8f18d63546aff2fbb1 100644 (file)
@@ -78,40 +78,40 @@ export declare class Account {
        * derived and saved.
        *
        * @param {string} privateKey - Private key of the account
-       * @param {(string|Uint8Array)} password - Used to encrypt the private key
+       * @param {Key} password - Used to encrypt the private key
        * @returns {Account} A new Account object
        */
-       static import (privateKey: string, password: string | Uint8Array<ArrayBuffer>): Promise<Account>
+       static import (privateKey: string, password: Key): Promise<Account>
        /**
        * Instantiates an Account object from its private key which is then encrypted
        * and stored in IndexedDB. The corresponding public key will automatically be
        * derived and saved.
        *
        * @param {Uint8Array} privateKey - Private key of the account
-       * @param {(string|Uint8Array)} password - Used to encrypt the private key
+       * @param {Key} password - Used to encrypt the private key
        * @returns {Account} A new Account object
        */
-       static import (privateKey: Uint8Array<ArrayBuffer>, password: string | Uint8Array<ArrayBuffer>): Promise<Account>
+       static import (privateKey: Uint8Array<ArrayBuffer>, password: Key): Promise<Account>
        /**
        * Instantiates Account objects from their private keys which are then
        * encrypted and stored in IndexedDB. The corresponding public keys will
        * automatically be derived and saved.
        *
        * @param {string[]} privateKeys - Private keys of the account
-       * @param {(string|Uint8Array)} password - Used to encrypt the private keys
+       * @param {Key} password - Used to encrypt the private keys
        * @returns {Account[]} The instantiated Account objects
        */
-       static import (privateKeys: string[], password: string | Uint8Array<ArrayBuffer>): Promise<Account[]>
+       static import (privateKeys: string[], password: Key): Promise<Account[]>
        /**
        * Instantiates Account objects from their private keys which are then
        * encrypted and stored in IndexedDB. The corresponding public keys will
        * automatically be derived and saved.
        *
        * @param {Uint8Array[]} privateKeys - Private keys of the account
-       * @param {(string|Uint8Array)} password - Used to encrypt the private keys
+       * @param {Key} password - Used to encrypt the private keys
        * @returns {Account[]} The instantiated Account objects
        */
-       static import (privateKeys: Uint8Array<ArrayBuffer>[], password: string | Uint8Array<ArrayBuffer>): Promise<Account[]>
+       static import (privateKeys: Uint8Array<ArrayBuffer>[], password: Key): Promise<Account[]>
        /**
        * Refreshes the account from its current state on the network.
        *
@@ -125,27 +125,27 @@ export declare class Account {
        * Signs a block using the private key of the account. The signature is
        * appended to the signature field of the block before being returned.
        *
-       * @param {(string|Uint8Array)} password - Required to decrypt the private key for signing
+       * @param {Key} password - Required to decrypt the private key for signing
        * @param {(ChangeBlock|ReceiveBlock|SendBlock)} block - The block data to be hashed and signed
        * @returns {Promise<string>} Hexadecimal-formatted 64-byte signature
        */
-       sign (block: ChangeBlock | ReceiveBlock | SendBlock, password: string | Uint8Array<ArrayBuffer>): Promise<string>
+       sign (block: ChangeBlock | ReceiveBlock | SendBlock, password: Key): Promise<string>
        /**
        * Retrieves and decryptes the private key of the Account. The same password
        * used to lock it must be used to unlock it.
        *
-       * @param {(string|Uint8Array)} password Used previously to lock the Account
+       * @param {Key} password Used previously to lock the Account
        * @returns Private key bytes as a Uint8Array
        */
-       exportPrivateKey (password: string | Uint8Array<ArrayBuffer>): Promise<Uint8Array<ArrayBuffer>>
+       exportPrivateKey (password: Key): Promise<Uint8Array<ArrayBuffer>>
        /**
        * Retrieves and decryptes the private key of the Account. The same password
        * used to lock it must be used to unlock it.
        *
-       * @param {(string|Uint8Array)} password Used previously to lock the Account
+       * @param {Key} password Used previously to lock the Account
        * @returns Private key bytes as a hexadecimal string
        */
-       exportPrivateKey (password: string | Uint8Array<ArrayBuffer>, format: 'hex'): Promise<string>
+       exportPrivateKey (password: Key, format: 'hex'): Promise<string>
        /**
        * Validates a Nano address with 'nano' and 'xrb' prefixes
        * Derived from https://github.com/alecrios/nano-address-validator