From d3295d3100d31b8a45467f7ec13a713d2b22cb0a Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Sat, 8 Aug 2026 14:17:37 -0700 Subject: [PATCH] Refactor address validation as boolean instead of assertion. --- src/lib/account/index.ts | 2 +- src/lib/account/validate.ts | 6 ++---- src/lib/rpc/accounts_balances.ts | 12 ++++++++---- src/lib/rpc/accounts_frontiers.ts | 15 ++++++++++----- src/lib/rpc/block_info.ts | 4 ++-- src/lib/rpc/blocks_info.ts | 2 +- src/lib/wallet/refresh.ts | 8 +++++--- 7 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/lib/account/index.ts b/src/lib/account/index.ts index 5560878..9d779a1 100644 --- a/src/lib/account/index.ts +++ b/src/lib/account/index.ts @@ -230,7 +230,7 @@ export class Account { * @param {unknown} address - Nano address to validate * @throws Error if address is undefined, not a string, or an invalid format */ - static validate (address: unknown): asserts address is string { + static isValid (address: unknown): address is string { return _validate(address) } } diff --git a/src/lib/account/validate.ts b/src/lib/account/validate.ts index 23fe218..c1d7f05 100644 --- a/src/lib/account/validate.ts +++ b/src/lib/account/validate.ts @@ -7,7 +7,7 @@ import { Blake2b } from "../crypto" const pattern = new RegExp(`^(${PREFIX}|${PREFIX_LEGACY})[13][${ALPHABET}]{59}$`) -export function _validate (address: unknown): asserts address is string { +export function _validate (address: unknown): address is string { if (address === undefined) { throw new ReferenceError('Address is undefined.') } @@ -25,7 +25,5 @@ export function _validate (address: unknown): asserts address is string { actualChecksumBuf.reverse() const actualChecksum = bytes.toBase32(actualChecksumBuf) - if (expectedChecksum !== actualChecksum) { - throw new Error('Incorrect address checksum') - } + return expectedChecksum === actualChecksum } diff --git a/src/lib/rpc/accounts_balances.ts b/src/lib/rpc/accounts_balances.ts index 464f04a..3ab1fc8 100644 --- a/src/lib/rpc/accounts_balances.ts +++ b/src/lib/rpc/accounts_balances.ts @@ -21,9 +21,11 @@ export function accounts_balances (body: unknown): AccountsBalancesResponse { if ('errors' in body && body.errors != null && typeof body.errors === 'object') { for (const [address, error] of Object.entries(body.errors) as [string, unknown][]) { try { - Account.validate(address) + if (!Account.isValid(address)) { + throw new RpcError('Invalid account address') + } if (typeof error === 'string') { - throw new Error(error) + throw new RpcError(error) } } catch (err: any) { response.errors ??= {} @@ -35,9 +37,11 @@ export function accounts_balances (body: unknown): AccountsBalancesResponse { if ('balances' in body && body.balances != null && typeof body.balances === 'object') { for (const [address, data] of Object.entries(body.balances) as [string, unknown][]) { try { - Account.validate(address) + if (!Account.isValid(address)) { + throw new RpcError('Invalid account address') + } if (data == null || typeof data !== 'object') { - throw new Error('Invalid account balance data') + throw new RpcError('Invalid account balance data') } response.balances ??= {} response.balances[address] = account_balance(data) diff --git a/src/lib/rpc/accounts_frontiers.ts b/src/lib/rpc/accounts_frontiers.ts index 35fdb83..56c6955 100644 --- a/src/lib/rpc/accounts_frontiers.ts +++ b/src/lib/rpc/accounts_frontiers.ts @@ -3,8 +3,9 @@ import { Account } from '../account' import { hex } from '../convert' +import { RpcError } from '../errors' -type AccountsFrontiers = { +export type AccountsFrontiersResponse = { frontiers?: { [address: string]: Hex } @@ -13,8 +14,8 @@ type AccountsFrontiers = { } } -export function accounts_frontiers (body: unknown): AccountsFrontiers { - const response: AccountsFrontiers = {} +export function accounts_frontiers (body: unknown): AccountsFrontiersResponse { + const response: AccountsFrontiersResponse = {} if (body == null || typeof body !== 'object') { return response } @@ -25,7 +26,9 @@ export function accounts_frontiers (body: unknown): AccountsFrontiers { if (typeof frontiers === 'object') { for (const [address, frontier] of Object.entries(frontiers) as [string, unknown][]) { try { - Account.validate(address) + if (!Account.isValid(address)) { + throw new RpcError('Invalid account address') + } if (hex.is(frontier, 64)) { response.frontiers ??= {} response.frontiers[address] = frontier @@ -42,7 +45,9 @@ export function accounts_frontiers (body: unknown): AccountsFrontiers { if (typeof errors === 'object') { for (const [address, error] of Object.entries(errors) as [string, unknown][]) { try { - Account.validate(address) + if (!Account.isValid(address)) { + throw new RpcError('Invalid account address') + } if (typeof error === 'string') { throw new Error(error) } diff --git a/src/lib/rpc/block_info.ts b/src/lib/rpc/block_info.ts index eaa2e3c..fdbdc67 100644 --- a/src/lib/rpc/block_info.ts +++ b/src/lib/rpc/block_info.ts @@ -51,9 +51,9 @@ export function block_info (body: unknown): BlockInfoResponse { && 'account' in body.contents && typeof body.contents.account === 'string' && 'balance' in body.contents && typeof body.contents.balance === 'string' && 'link' in body.contents && hex.is(body.contents.link, 64) - && 'link_as_account' in body.contents && Account.validate(body.contents.link_as_account) + && 'link_as_account' in body.contents && Account.isValid(body.contents.link_as_account) && 'previous' in body.contents && hex.is(body.contents.previous, 64) - && 'representative' in body.contents && typeof body.contents.representative === 'string' + && 'representative' in body.contents && Account.isValid(body.contents.representative) && 'signature' in body.contents && hex.is(body.contents.signature, 128) && 'type' in body.contents && body.contents.type === 'state' && 'work' in body.contents && typeof body.contents.work === 'string' diff --git a/src/lib/rpc/blocks_info.ts b/src/lib/rpc/blocks_info.ts index cc8bc35..12de6e5 100644 --- a/src/lib/rpc/blocks_info.ts +++ b/src/lib/rpc/blocks_info.ts @@ -6,7 +6,7 @@ import { hex } from '../convert' import { RpcError } from '../errors' import { BlockInfoResponse, block_info } from './block_info' -type BlocksInfoResponse = { +export type BlocksInfoResponse = { blocks?: { [hash: Hex]: BlockInfoResponse } diff --git a/src/lib/wallet/refresh.ts b/src/lib/wallet/refresh.ts index 25c1f32..28473a2 100644 --- a/src/lib/wallet/refresh.ts +++ b/src/lib/wallet/refresh.ts @@ -5,6 +5,8 @@ import { Account } from '../account' import { Block } from '../block' import { Rpc } from '../rpc' import { AccountInfoRequest, AccountInfoResponse } from '../rpc/account_info' +import { AccountsFrontiersResponse } from '../rpc/accounts_frontiers' +import { BlocksInfoResponse } from '../rpc/blocks_info' import { Wallet } from '../wallet' export async function _refresh (wallet: Wallet, rpc: Rpc | string | URL, from: number, to: number): Promise> @@ -29,8 +31,8 @@ export async function _refresh (wallet: Wallet, rpc: unknown, from: unknown, to: json_block: true, accounts: addresses } - const { frontiers } = await rpc.post('accounts_frontiers', data) - const { blocks } = await rpc.post('blocks_info', { json_block: true, include_not_found: true, hashes: Object.values(frontiers ?? {}) }) + const { frontiers }: AccountsFrontiersResponse = await rpc.post('accounts_frontiers', data) + const { blocks }: BlocksInfoResponse = await rpc.post('blocks_info', { json_block: true, include_not_found: true, hashes: Object.values(frontiers ?? {}) }) for (const account of accounts.values()) { const reqAccountInfo: AccountInfoRequest = { @@ -59,7 +61,7 @@ export async function _refresh (wallet: Wallet, rpc: unknown, from: unknown, to: if (typeof frontierBlock[subtype] !== 'function') { throw new TypeError('Unknown frontier block subtype', { cause: subtype }) } - const arg = subtype === 'epoch' ? account.version : subtype === 'change' ? representative : link + const arg = subtype === 'epoch' ? account_version : subtype === 'change' ? representative : link frontierBlock[subtype](arg, 0).signature = signature account.frontier_block = frontierBlock } -- 2.52.0