]> git.codecow.com Git - libnemo.git/commitdiff
Deprecate AccountList for built-in Map.
authorChris Duncan <chris@zoso.dev>
Sat, 6 Sep 2025 04:54:23 +0000 (21:54 -0700)
committerChris Duncan <chris@zoso.dev>
Sat, 6 Sep 2025 04:54:23 +0000 (21:54 -0700)
src/lib/account/index.ts
src/lib/tools.ts
src/lib/wallet/accounts.ts
src/lib/wallet/index.ts
src/lib/wallet/refresh.ts
src/lib/wallet/unopened.ts
src/types.d.ts
test/test.derive-accounts.mjs
test/test.import-wallet.mjs
test/test.refresh-accounts.mjs

index 222536fb1c16e9f4f6d1f4359e87554d8edf26b0..612bb2a156c5ee420cbb1674334d967d0969a59f 100644 (file)
@@ -353,25 +353,3 @@ export class Account {
                return true\r
        }\r
 }\r
-\r
-export class AccountList extends Object {\r
-       [index: number]: Account\r
-\r
-       get length (): number {\r
-               return Object.keys(this).length\r
-       }\r
-\r
-       *[Symbol.iterator] (): Iterator<Account> {\r
-               const indexes = Object.keys(this)\r
-                       .map(key => parseInt(key))\r
-                       .filter(index => Number.isFinite(index))\r
-                       .sort((a, b) => a - b)\r
-               for (const i of indexes) {\r
-                       yield this[i]\r
-               }\r
-       }\r
-\r
-       static {\r
-               Object.defineProperty(this, 'length', { enumerable: false })\r
-       }\r
-}\r
index db16d7f49b8d587e4c71c3286ec395076f0b65e6..7ab2c9fc30f2d648776986ff02772432f358a06a 100644 (file)
@@ -163,16 +163,16 @@ export async function sweep (
        const recipientAccount = Account.load(recipient)
        const accounts = await wallet.refresh(rpc, from, to)
 
-       for (const account of accounts) {
+       for (const [index, account] of accounts) {
                const blockRequest: Promise<void> = new Promise(async (resolve) => {
                        let block
                        try {
-                               if (account.index == null) {
+                               if (index == null && account.index == null) {
                                        throw new TypeError('Account index is required', { cause: account })
                                }
                                block = await new Block(account)
                                        .send(recipientAccount, account.balance ?? 0n)
-                                       .sign(wallet, account.index)
+                                       .sign(wallet, account.index ?? index)
                                await block.pow()
                                const hash = await block.process(rpc)
                                results.push({ status: 'success', address: block.account.address, message: hash })
index f5aa7207bf441586d750c2f1ea9ffa46bec5157f..d5331e289334322aba6c002c62ffc00b3c72f9c3 100644 (file)
@@ -2,22 +2,24 @@
 //! SPDX-License-Identifier: GPL-3.0-or-later
 
 import { KeyPair, WalletType } from '#types'
-import { Account, AccountList } from '../account'
+import { Account } from '../account'
 import { Vault } from '../vault'
 
-export async function _accounts (type: WalletType, accounts: AccountList, vault: Vault, from: number, to: number): Promise<AccountList>
-export async function _accounts (type: WalletType, accounts: AccountList, vault: Vault, from: unknown, to: unknown): Promise<AccountList> {
+export async function _accounts (type: WalletType, accounts: Map<number, Account>, vault: Vault, index: number): Promise<Account>
+export async function _accounts (type: WalletType, accounts: Map<number, Account>, vault: Vault, from: number, to: number): Promise<Map<number, Account>>
+export async function _accounts (type: WalletType, accounts: Map<number, Account>, vault: Vault, from: unknown, to: unknown = from): Promise<Account | Map<number, Account>> {
        if (typeof from !== 'number' || typeof to !== 'number') {
                throw new TypeError('Invalid account range', { cause: `${from}-${to}` })
        }
        if (from > to) [from as number, to as number] = [to, from]
-       const output = new AccountList()
+       const output = new Map<number, Account>()
        const indexes: number[] = []
        for (let i = from; i <= to; i++) {
-               if (accounts[i] == null) {
+               const account = accounts.get(i)
+               if (account == null) {
                        indexes.push(i)
                } else {
-                       output[i] = accounts[i]
+                       output.set(i, account)
                }
        }
        if (indexes.length > 0) {
@@ -48,8 +50,16 @@ export async function _accounts (type: WalletType, accounts: AccountList, vault:
                        if (a.index == null) {
                                throw new RangeError('Index missing for Account')
                        }
-                       output[a.index] = accounts[a.index] = a
+                       output.set(a.index, a)
+                       accounts.set(a.index, a)
                }
        }
+       if (from === to) {
+               const account = output.get(from)
+               if (account === undefined) {
+                       throw new Error(`Failed to get account at index ${from}`)
+               }
+               return account
+       }
        return output
 }
index b0365713b0719426f4d59960d41d32140a6d340e..6f5ecfe971b8eb24fb2e7223e3f99db312363c51 100644 (file)
@@ -2,7 +2,7 @@
 //! SPDX-License-Identifier: GPL-3.0-or-later\r
 \r
 import { NamedData, WalletType } from '#types'\r
-import { Account, AccountList } from '../account'\r
+import { Account } from '../account'\r
 import { Block } from '../block'\r
 import { ADDRESS_GAP } from '../constants'\r
 import { bytes } from '../convert'\r
@@ -124,7 +124,7 @@ export class Wallet {
                        throw new TypeError('Invalid wallet type', { cause: type })\r
                }\r
                Wallet.#isInternal = false\r
-               this.#accounts = new AccountList()\r
+               this.#accounts = new Map<number, Account>()\r
                this.#id = id ?? crypto.randomUUID()\r
                this.#type = type\r
                this.#vault = new Vault()\r
@@ -195,7 +195,7 @@ export class Wallet {
        * @returns Promise for the Account at the specified index\r
        */\r
        async account (index: number = 0): Promise<Account> {\r
-               return (await this.accounts(index))[index]\r
+               return await _accounts(this.type, this.#accounts, this.#vault, index)\r
        }\r
 \r
        /**\r
@@ -232,9 +232,9 @@ export class Wallet {
        * If `from` is greater than `to`, their values will be swapped.\r
        * @param {number} [from=0] - Start index of accounts. Default: 0\r
        * @param {number} [to=from] - End index of accounts. Default: `from`\r
-       * @returns {Promise<AccountList>} Promise for a list of Accounts at the specified indexes\r
+       * @returns {Promise<Map<number, Account>>} Promise for a list of Accounts at the specified indexes\r
        */\r
-       async accounts (from: number = 0, to: number = from): Promise<AccountList> {\r
+       async accounts (from: number = 0, to: number = from): Promise<Map<number, Account>> {\r
                return await _accounts(this.type, this.#accounts, this.#vault, from, to)\r
        }\r
 \r
@@ -265,9 +265,9 @@ export class Wallet {
        * A successful response will set these properties on each account.\r
        *\r
        * @param {(Rpc|string|URL)} rpc - RPC node information required to refresh accounts, calculate PoW, and process blocks\r
-       * @returns {Promise<AccountList>} Promise for accounts with updated balances, frontiers, and representatives\r
+       * @returns {Promise<Map<number, Account>>} Promise for accounts with updated balances, frontiers, and representatives\r
        */\r
-       async refresh (rpc: Rpc | string | URL, from: number = 0, to: number = from): Promise<AccountList> {\r
+       async refresh (rpc: Rpc | string | URL, from: number = 0, to: number = from): Promise<Map<number, Account>> {\r
                return await _refresh(this, rpc, from, to)\r
        }\r
 \r
@@ -366,7 +366,7 @@ export class Wallet {
        }\r
 \r
        static #isInternal: boolean = false\r
-       #accounts: AccountList\r
+       #accounts: Map<number, Account>\r
        #id: string\r
        #mnemonic?: ArrayBuffer\r
        #seed?: ArrayBuffer\r
index 69b5651ed33d3be74a2aaf3868c9575dbe86729e..3166a81a80771a28ffd5807320005d3efe982e51 100644 (file)
@@ -2,13 +2,13 @@
 //! SPDX-License-Identifier: GPL-3.0-or-later
 
 import { BlockInfo } from '#types'
-import { AccountList } from '../account'
+import { Account } from '../account'
 import { Block } from '../block'
 import { Rpc } from '../rpc'
 import { Wallet } from '../wallet'
 
-export async function _refresh (wallet: Wallet, rpc: Rpc | string | URL, from: number, to: number): Promise<AccountList>
-export async function _refresh (wallet: Wallet, rpc: unknown, from: unknown, to: unknown): Promise<AccountList> {
+export async function _refresh (wallet: Wallet, rpc: Rpc | string | URL, from: number, to: number): Promise<Map<number, Account>>
+export async function _refresh (wallet: Wallet, rpc: unknown, from: unknown, to: unknown): Promise<Map<number, Account>> {
        try {
                if (typeof rpc === 'string' || rpc instanceof URL) {
                        rpc = new Rpc(rpc)
@@ -22,7 +22,7 @@ export async function _refresh (wallet: Wallet, rpc: unknown, from: unknown, to:
                if (from > to) [from as number, to as number] = [to, from]
                const accounts = await wallet.accounts(from, to)
                const addresses = []
-               for (const account of accounts) {
+               for (const [index, account] of accounts) {
                        addresses.push(account.address)
                }
                const data = {
@@ -33,7 +33,7 @@ export async function _refresh (wallet: Wallet, rpc: unknown, from: unknown, to:
                const { frontiers } = await rpc.call('accounts_frontiers', data) as { frontiers: { [address: string]: string } }
                const { representatives } = await rpc.call('accounts_representatives', data) as { representatives: { [address: string]: string } }
                const { blocks } = await rpc.call('blocks_info', { json_block: true, hashes: Object.values(frontiers) }) as BlockInfo
-               for (const account of accounts) {
+               for (const [index, account] of accounts) {
                        account.balance = balances[account.address]?.balance
                        account.receivable = balances[account.address]?.receivable
                        account.representative = representatives?.[account.address]
index 6023bb808d65c5b3ef454149133d0c1cd8c88ffe..6ffaa3be825b4e0b585686421bc30f14c571a788 100644 (file)
@@ -19,7 +19,7 @@ export async function _unopened (wallet: Wallet, rpc: unknown, batchSize: unknow
        const to = from + batchSize
        const accounts = await wallet.accounts(from, to - 1)
        const addresses = []
-       for (const account of accounts) {
+       for (const [index, account] of accounts) {
                addresses.push(account.address)
        }
        const data = {
@@ -27,11 +27,13 @@ export async function _unopened (wallet: Wallet, rpc: unknown, batchSize: unknow
        }
        const { errors } = await rpc.call('accounts_frontiers', data)
        if (errors != null) {
-               for (let i = from; i < to; i++) {
-                       const account = accounts[i]
-                       const value = errors[account.address]
+               for (const a of addresses) {
+                       const value = errors[a]
                        if (value === 'Account not found') {
-                               return account
+                               const account = accounts.values().find(({ address }) => a === address)
+                               if (account !== undefined) {
+                                       return account
+                               }
                        }
                }
        }
index 7bd8d6f9e7ac70cbfd9d9e6fb7c0e025a7057e0b..ae0c0ab7bc0f02392a663ff13bda8927d09c8c64 100644 (file)
@@ -135,11 +135,6 @@ export declare class Account {
        */
        static validate (address: string): asserts address is string
 }
-export declare class AccountList extends Object {
-       [index: number]: Account
-       get length (): number
-       [Symbol.iterator] (): Iterator<Account>
-}
 
 /**
 * Represents a mnemonic phrase that identifies a wallet as defined by BIP-39.
@@ -527,6 +522,7 @@ export declare function convert (amount: bigint | number | string, inputUnit: st
 declare function hash (data: string | string[], encoding?: 'hex'): Uint8Array<ArrayBuffer>
 /**
 * Signs arbitrary strings with a private key using the Ed25519 signature scheme.
+* The strings are first hashed to a 32-byte value using BLAKE2b.
 *
 * @param {string | Uint8Array<ArrayBuffer>} key - Hexadecimal-formatted private key to use for signing
 * @param {...string} input - Data to be signed
@@ -704,7 +700,7 @@ export declare class Wallet {
        * @param {number} [to=from] - End index of accounts. Default: `from`
        * @returns {Promise<AccountList>} Promise for a list of Accounts at the specified indexes
        */
-       accounts (from?: number, to?: number): Promise<AccountList>
+       accounts (from?: number, to?: number): Promise<Map<number, Account>>
        /**
        * Removes encrypted secrets in storage, releases variable references to
        * allow garbage collection, and terminates vault worker.
@@ -728,7 +724,7 @@ export declare class Wallet {
        * @param {(Rpc|string|URL)} rpc - RPC node information required to refresh accounts, calculate PoW, and process blocks
        * @returns {Promise<AccountList>} Promise for accounts with updated balances, frontiers, and representatives
        */
-       refresh (rpc: Rpc | string | URL, from?: number, to?: number): Promise<AccountList>
+       refresh (rpc: Rpc | string | URL, from?: number, to?: number): Promise<Map<number, Account>>
        /**
        * Signs arbitrary strings using the private key of the account at the wallet
        * index specified and returns a detached signature as a hexadecimal string.
index 1fee8fd04d74bad78a0d84138b9aeb2a3b2bdc8a..bc0cde775dbd1382b30180a01a829af35ddef29e 100644 (file)
@@ -40,7 +40,7 @@ await Promise.all([
                        await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
                        const accounts = await wallet.accounts(1, 2)\r
 \r
-                       assert.equal(accounts.length, 2)\r
+                       assert.equal(accounts.size, 2)\r
                        assert.equal(accounts[1].publicKey, NANO_TEST_VECTORS.PUBLIC_1)\r
                        assert.equal(accounts[1].address, NANO_TEST_VECTORS.ADDRESS_1)\r
                        assert.equal(accounts[1].index, 1)\r
@@ -56,7 +56,7 @@ await Promise.all([
                        await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
                        const accounts = await wallet.accounts(0x70000000, 0x7000000f)\r
 \r
-                       assert.equal(accounts.length, 0x10)\r
+                       assert.equal(accounts.size, 0x10)\r
                        for (let i = 0x70000000; i < 0x7000000f; i++) {\r
                                const a = accounts[i]\r
                                assert.exists(a)\r
@@ -94,7 +94,7 @@ await Promise.all([
                        await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
                        const accounts = await wallet.accounts(2, 3)\r
 \r
-                       assert.equal(accounts.length, 2)\r
+                       assert.equal(accounts.size, 2)\r
                        assert.exists(accounts[2].publicKey)\r
                        assert.exists(accounts[2].address)\r
                        assert.equal(accounts[2].index, 2)\r
@@ -110,7 +110,7 @@ await Promise.all([
                        await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
                        const accounts = await wallet.accounts(0x70000000, 0x7000000f)\r
 \r
-                       assert.equal(accounts.length, 0x10)\r
+                       assert.equal(accounts.size, 0x10)\r
                        for (let i = 0x70000000; i < 0x7000000f; i++) {\r
                                const a = accounts[i]\r
                                assert.exists(a)\r
index 37b389c3959d4b5f334ebd36e0af28e189b54afa..b5fdf770c4db59058dd615b59d48b9b24cc2c771 100644 (file)
@@ -114,9 +114,9 @@ await Promise.all([
                        assert.ok('seed' in wallet)\r
                        assert.ok(await wallet.verify(TREZOR_TEST_VECTORS.MNEMONIC_0))\r
                        assert.ok(await wallet.verify(TREZOR_TEST_VECTORS.SEED_0))\r
-                       assert.equal(accounts.length, 4)\r
+                       assert.equal(accounts.size, 4)\r
 \r
-                       for (let i = 0; i < accounts.length; i++) {\r
+                       for (let i = 0; i < accounts.size; i++) {\r
                                assert.exists(accounts[i])\r
                                assert.exists(accounts[i].address)\r
                                assert.exists(accounts[i].publicKey)\r
@@ -136,9 +136,9 @@ await Promise.all([
                        assert.ok(wallet.seed === undefined)\r
                        assert.ok(await wallet.verify(TREZOR_TEST_VECTORS.MNEMONIC_0))\r
                        assert.ok(await wallet.verify(TREZOR_TEST_VECTORS.ENTROPY_0))\r
-                       assert.equal(accounts.length, 4)\r
+                       assert.equal(accounts.size, 4)\r
 \r
-                       for (let i = 0; i < accounts.length; i++) {\r
+                       for (let i = 0; i < accounts.size; i++) {\r
                                assert.exists(accounts[i])\r
                                assert.exists(accounts[i].address)\r
                                assert.exists(accounts[i].publicKey)\r
index af7b78e61893ba11c31193c18b83c79a51cc529f..65443402f28120582663e6b493160559a4fcfabc 100644 (file)
@@ -38,7 +38,6 @@ await Promise.all([
                        assert.exists(account.balance)
                        assert.notEqual(account.balance, '')
                        assert.equal(typeof account.balance, 'bigint')
-                       //@ts-expect-error
                        assert.ok(account.balance >= 0n)
 
                        assert.exists(account.frontier)
@@ -184,13 +183,14 @@ await Promise.all([
                        await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)
                        const accounts = await wallet.refresh(rpc, 0, 2)
 
-                       assert.equal(accounts.length, 3)
+                       assert.equal(accounts.size, 3)
                        for (let i = 0; i < 2; i++) {
-                               assert.exists(accounts[i])
-                               assert.ok(accounts[i] instanceof Account)
-                               assert.equal(accounts[i].index, i)
-                               assert.equal(accounts[i].balance, 0n)
-                               assert.equal(accounts[i].receivable, 0n)
+                               const account = accounts.get(i)
+                               assert.exists(account)
+                               assert.ok(account instanceof Account)
+                               assert.equal(account.index, i)
+                               assert.equal(account.balance, 0n)
+                               assert.equal(account.receivable, 0n)
                        }
                        assert.equal(accounts[0].address, 'nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94nkbxe8g4a6x1p69emk8y1d')
                        assert.equal(accounts[1].address, 'nano_3phqgrqbso99xojkb1bijmfryo7dy1k38ep1o3k3yrhb7rqu1h1k47yu78gz')