From 8c10a24b782f84b2ab457cd05a0d58a67e89a241 Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Sat, 5 Jul 2025 22:06:15 -0700 Subject: [PATCH] Rename getNextNewAccount and alphabetize wallet methods. --- src/lib/wallets/wallet.ts | 144 ++++++++++++++++----------------- test/test.refresh-accounts.mjs | 26 +++--- 2 files changed, 85 insertions(+), 85 deletions(-) diff --git a/src/lib/wallets/wallet.ts b/src/lib/wallets/wallet.ts index 7bbc335..4a68517 100644 --- a/src/lib/wallets/wallet.ts +++ b/src/lib/wallets/wallet.ts @@ -53,25 +53,6 @@ export abstract class Wallet { Wallet.#poolSafe ??= new Pool(SafeWorker) } - /** - * Removes encrypted secrets in storage and releases variable references to - * allow garbage collection. - */ - async destroy (): Promise { - let i = 0 - for (const a in this.#accounts) { - await this.#accounts[a].destroy() - delete this.#accounts[a] - i++ - } - this.#m = null - this.#s = null - await Wallet.#poolSafe.assign({ - method: 'destroy', - name: this.id - }) - } - /** * Retrieves an account from a wallet using its child key derivation function. * Defaults to the first account at index 0. @@ -152,61 +133,22 @@ export abstract class Wallet { } /** - * Fetches the lowest-indexed unopened account from a wallet in sequential - * order. An account is unopened if it has no frontier block. - * - * @param {Rpc|string|URL} rpc - RPC node information required to refresh accounts, calculate PoW, and process blocks - * @param {number} batchSize - Number of accounts to fetch and check per RPC callout - * @param {number} from - Account index from which to start the search - * @returns {Promise} The lowest-indexed unopened account belonging to the wallet - */ - async getNextNewAccount (rpc: Rpc, batchSize: number = ADDRESS_GAP, from: number = 0): Promise { - if (!Number.isSafeInteger(batchSize) || batchSize < 1) { - throw new RangeError(`Invalid batch size ${batchSize}`) - } - const accounts = await this.accounts(from, from + batchSize - 1) - const addresses = [] - for (const a in accounts) { - addresses.push(accounts[a].address) - } - const data = { - "accounts": addresses - } - const { errors } = await rpc.call('accounts_frontiers', data) - for (const key of Object.keys(errors ?? {})) { - const value = errors[key] - if (value === 'Account not found') { - return Account.fromAddress(key) - } - } - return await this.getNextNewAccount(rpc, batchSize, from + batchSize) - } - - /** - * Refreshes wallet account balances, frontiers, and representatives from the - * current state on the network. - * - * A successful response will set these properties on each account. - * - * @param {Rpc|string|URL} rpc - RPC node information required to refresh accounts, calculate PoW, and process blocks - * @returns {Promise} Accounts with updated balances, frontiers, and representatives + * Removes encrypted secrets in storage and releases variable references to + * allow garbage collection. */ - async refresh (rpc: Rpc | string | URL, from: number = 0, to: number = from): Promise { - if (typeof rpc === 'string' || rpc.constructor === URL) { - rpc = new Rpc(rpc) - } - if (rpc.constructor !== Rpc) { - throw new TypeError('RPC must be a valid node') - } - const accounts = await this.accounts(from, to) - for (const a in accounts) { - try { - await accounts[a].refresh(rpc) - } catch (err) { - delete accounts[a] - } + async destroy (): Promise { + let i = 0 + for (const a in this.#accounts) { + await this.#accounts[a].destroy() + delete this.#accounts[a] + i++ } - return accounts + this.#m = null + this.#s = null + await Wallet.#poolSafe.assign({ + method: 'destroy', + name: this.id + }) } /** @@ -260,6 +202,33 @@ export abstract class Wallet { return true } + /** + * Refreshes wallet account balances, frontiers, and representatives from the + * current state on the network. + * + * A successful response will set these properties on each account. + * + * @param {Rpc|string|URL} rpc - RPC node information required to refresh accounts, calculate PoW, and process blocks + * @returns {Promise} Accounts with updated balances, frontiers, and representatives + */ + async refresh (rpc: Rpc | string | URL, from: number = 0, to: number = from): Promise { + if (typeof rpc === 'string' || rpc.constructor === URL) { + rpc = new Rpc(rpc) + } + if (rpc.constructor !== Rpc) { + throw new TypeError('RPC must be a valid node') + } + const accounts = await this.accounts(from, to) + for (const a in accounts) { + try { + await accounts[a].refresh(rpc) + } catch (err) { + delete accounts[a] + } + } + return accounts + } + /** * Unlocks the wallet using the same password as used prior to lock it. * @@ -302,4 +271,35 @@ export abstract class Wallet { } return true } + + /** + * Fetches the lowest-indexed unopened account from a wallet in sequential + * order. An account is unopened if it has no frontier block. + * + * @param {Rpc|string|URL} rpc - RPC node information required to refresh accounts, calculate PoW, and process blocks + * @param {number} batchSize - Number of accounts to fetch and check per RPC callout + * @param {number} from - Account index from which to start the search + * @returns {Promise} The lowest-indexed unopened account belonging to the wallet + */ + async unopened (rpc: Rpc, batchSize: number = ADDRESS_GAP, from: number = 0): Promise { + if (!Number.isSafeInteger(batchSize) || batchSize < 1) { + throw new RangeError(`Invalid batch size ${batchSize}`) + } + const accounts = await this.accounts(from, from + batchSize - 1) + const addresses = [] + for (const a in accounts) { + addresses.push(accounts[a].address) + } + const data = { + "accounts": addresses + } + const { errors } = await rpc.call('accounts_frontiers', data) + for (const key of Object.keys(errors ?? {})) { + const value = errors[key] + if (value === 'Account not found') { + return Account.fromAddress(key) + } + } + return await this.unopened(rpc, batchSize, from + batchSize) + } } diff --git a/test/test.refresh-accounts.mjs b/test/test.refresh-accounts.mjs index c37f34b..d325f09 100644 --- a/test/test.refresh-accounts.mjs +++ b/test/test.refresh-accounts.mjs @@ -78,7 +78,7 @@ await suite('Fetch next unopened account', { skip: false }, async () => { await test('return correct account from test vector', async () => { const wallet = await Bip44Wallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) - const account = await wallet.getNextNewAccount(rpc) + const account = await wallet.unopened(rpc) assert.exists(account) assert.equals(account.address, NANO_TEST_VECTORS.ADDRESS_1) @@ -90,7 +90,7 @@ await suite('Fetch next unopened account', { skip: false }, async () => { await test('return successfully for small batch size', async () => { const wallet = await Bip44Wallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) - const account = await wallet.getNextNewAccount(rpc, 1) + const account = await wallet.unopened(rpc, 1) assert.exists(account) assert.equals(account.address, NANO_TEST_VECTORS.ADDRESS_1) @@ -102,7 +102,7 @@ await suite('Fetch next unopened account', { skip: false }, async () => { await test('return successfully for large batch size', async () => { const wallet = await Bip44Wallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) - const account = await wallet.getNextNewAccount(rpc, 100) + const account = await wallet.unopened(rpc, 100) assert.exists(account) assert.equals(account.address, NANO_TEST_VECTORS.ADDRESS_1) @@ -115,11 +115,11 @@ await suite('Fetch next unopened account', { skip: false }, async () => { const wallet = await Bip44Wallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) - await assert.rejects(wallet.getNextNewAccount()) - await assert.rejects(wallet.getNextNewAccount(null)) - await assert.rejects(wallet.getNextNewAccount(1)) - await assert.rejects(wallet.getNextNewAccount('')) - await assert.rejects(wallet.getNextNewAccount('foo')) + await assert.rejects(wallet.unopened()) + await assert.rejects(wallet.unopened(null)) + await assert.rejects(wallet.unopened(1)) + await assert.rejects(wallet.unopened('')) + await assert.rejects(wallet.unopened('foo')) await wallet.destroy() }) @@ -128,11 +128,11 @@ await suite('Fetch next unopened account', { skip: false }, async () => { const wallet = await Bip44Wallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) - await assert.rejects(wallet.getNextNewAccount(rpc, null)) - await assert.rejects(wallet.getNextNewAccount(rpc, -1)) - await assert.rejects(wallet.getNextNewAccount(rpc, '')) - await assert.rejects(wallet.getNextNewAccount(rpc, 'foo')) - await assert.rejects(wallet.getNextNewAccount(rpc, { 'foo': 'bar' })) + await assert.rejects(wallet.unopened(rpc, null)) + await assert.rejects(wallet.unopened(rpc, -1)) + await assert.rejects(wallet.unopened(rpc, '')) + await assert.rejects(wallet.unopened(rpc, 'foo')) + await assert.rejects(wallet.unopened(rpc, { 'foo': 'bar' })) await wallet.destroy() }) -- 2.47.3