From 5cfffe799dccd8feb7eaee1aaf44e7dade751967 Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Tue, 30 Jun 2026 22:30:47 -0700 Subject: [PATCH] Promote Bytes type alias from BIP-44 to global scope. --- src/index.ts | 4 ++++ src/lib/account/address.ts | 6 +++--- src/lib/account/index.ts | 10 +++++----- src/lib/block/index.ts | 6 +++--- src/lib/convert/base32.ts | 2 +- src/lib/convert/bytes.ts | 4 ++-- src/lib/convert/dec.ts | 2 +- src/lib/convert/hex.ts | 2 +- src/lib/convert/utf8.ts | 2 +- src/lib/crypto/bip39.ts | 24 ++++++++++++------------ src/lib/crypto/bip44.ts | 1 - src/lib/crypto/blake2b.ts | 14 +++++++------- src/lib/ledger/sign.ts | 4 ++-- src/lib/tools.ts | 12 ++++++------ src/lib/vault/vault-worker.ts | 2 +- 15 files changed, 49 insertions(+), 46 deletions(-) diff --git a/src/index.ts b/src/index.ts index 985f6be..641c1e7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,10 @@ import { Rpc } from './lib/rpc' import { Tools } from './lib/tools' import { Wallet } from './lib/wallet' +declare global { + type Bytes = Uint8Array +} + export { Account, Blake2b, diff --git a/src/lib/account/address.ts b/src/lib/account/address.ts index 5995847..83bfd27 100644 --- a/src/lib/account/address.ts +++ b/src/lib/account/address.ts @@ -14,7 +14,7 @@ export class Address { * @param {Uint8Array} publicKey - Public key bytes as Uint8Array * @returns Nano address string using `nano_` prefix */ - static fromPublicKey (publicKey: ArrayBuffer | Uint8Array): string { + static fromPublicKey (publicKey: ArrayBuffer | Bytes): string { const checksum = new Blake2b(5).update(publicKey).digest().reverse() const encodedPublicKey = bytes.toBase32(publicKey) const encodedChecksum = bytes.toBase32(checksum) @@ -22,7 +22,7 @@ export class Address { } constructor (address: string) - constructor (publicKey: string | ArrayBuffer | Uint8Array) + constructor (publicKey: string | ArrayBuffer | Bytes) constructor (value: unknown) { if (typeof value !== 'string' && !(value instanceof ArrayBuffer) && !(value instanceof Uint8Array)) { throw new TypeError('Invalid address input', { cause: value }) @@ -62,7 +62,7 @@ export class Address { * * @returns Public key bytes as ArrayBuffer */ - toPublicKey (): Uint8Array { + toPublicKey (): Bytes { const publicKey = base32.toBytes(this.#address.slice(-60, -8)) const checksum = base32.toBytes(this.#address.slice(-8)) const rechecksum = new Blake2b(5).update(publicKey).digest().reverse() diff --git a/src/lib/account/index.ts b/src/lib/account/index.ts index 75a1822..ef12304 100644 --- a/src/lib/account/index.ts +++ b/src/lib/account/index.ts @@ -22,7 +22,7 @@ export class Account { /** @returns {'Account'} */ static get DB_NAME (): 'Account' { return 'Account' } - #publicKey: Uint8Array = new Uint8Array(32) + #publicKey: Bytes = new Uint8Array(32) #address?: Address #confirmed_balance?: bigint @@ -157,21 +157,21 @@ export class Account { constructor (address: string) /** * Instantiates an Account object from its public key. - * @param {(string | Uint8Array)} publicKey - Public key of the account + * @param {(string | Bytes)} publicKey - Public key of the account * @returns {Account} A new Account object */ - constructor (publicKey: string | ArrayBuffer | Uint8Array) + constructor (publicKey: string | ArrayBuffer | Bytes) /** * Instantiates an Account object from its public or private key. * * If the key is indicated as private, then it is copied locally, used to * derive the corresponding public key, and finally zeroed out; the user is * responsible for securely handling the original input. - * @param {(string | Uint8Array)} key - Public or private key of the account + * @param {(string | Bytes)} key - Public or private key of the account * @param {string} type - Indicates which type the key is * @returns {Account} A new Account object */ - constructor (key: string | ArrayBuffer | Uint8Array, type: 'public' | 'private') + constructor (key: string | ArrayBuffer | Bytes, type: 'public' | 'private') constructor (input: unknown, type: unknown = 'public') { if (type === 'private') { try { diff --git a/src/lib/block/index.ts b/src/lib/block/index.ts index 7e0adad..9f348a2 100644 --- a/src/lib/block/index.ts +++ b/src/lib/block/index.ts @@ -20,7 +20,7 @@ import { _verify } from './verify' * Represents a block as defined by the Nano cryptocurrency protocol. */ export class Block { - [key: string]: bigint | string | Account | Function | Uint8Array | 'send' | 'receive' | 'change' | undefined + [key: string]: bigint | string | Account | Function | Bytes | 'send' | 'receive' | 'change' | undefined /** * Validates the format of Block properties. @@ -34,9 +34,9 @@ export class Block { subtype?: 'send' | 'receive' | 'change' account: Account balance: bigint - previous: Uint8Array + previous: Bytes representative?: Account - link?: Uint8Array + link?: Bytes signature?: string work?: string diff --git a/src/lib/convert/base32.ts b/src/lib/convert/base32.ts index 0896f39..008366b 100644 --- a/src/lib/convert/base32.ts +++ b/src/lib/convert/base32.ts @@ -10,7 +10,7 @@ export const base32 = Object.freeze({ * @param {string} base32 - String to convert * @returns {Uint8Array} Byte array representation of the input string */ - toBytes (base32: string): Uint8Array { + toBytes (base32: string): Bytes { const leftover = (base32.length * 5) % 8 const offset = leftover === 0 ? 0 diff --git a/src/lib/convert/bytes.ts b/src/lib/convert/bytes.ts index af27d45..acb6031 100644 --- a/src/lib/convert/bytes.ts +++ b/src/lib/convert/bytes.ts @@ -12,7 +12,7 @@ export const bytes = Object.freeze({ * * @param bytes - Buffer or bytes to erase */ - erase (bytes?: ArrayBuffer | Uint8Array | null): void { + erase (bytes?: ArrayBuffer | Bytes | null): void { if (bytes == null) return if (bytes instanceof ArrayBuffer && bytes.byteLength === 0) return if (bytes instanceof Uint8Array && bytes.buffer.byteLength === 0) return @@ -89,7 +89,7 @@ export const bytes = Object.freeze({ * @param {Uint8Array} bytes - Byte array to convert * @returns {string} UTF-8 encoded text string */ - toUtf8 (bytes: Uint8Array): string { + toUtf8 (bytes: Bytes): string { return decoder.decode(bytes) }, }) diff --git a/src/lib/convert/dec.ts b/src/lib/convert/dec.ts index 5f8f4fe..b69979b 100644 --- a/src/lib/convert/dec.ts +++ b/src/lib/convert/dec.ts @@ -10,7 +10,7 @@ export const dec = Object.freeze({ * @param {number} [padding=1] - Minimum length of the resulting array padded as necessary with starting 0x00 bytes * @returns {Uint8Array} Byte array representation of the input decimal */ - toBytes (decimal: bigint | number | string, padding: number = 1): Uint8Array { + toBytes (decimal: bigint | number | string, padding: number = 1): Bytes { if (decimal == null) { throw new TypeError(`Failed to convert '${decimal}' from decimal to bytes`) } diff --git a/src/lib/convert/hex.ts b/src/lib/convert/hex.ts index fb4360e..f37d4de 100644 --- a/src/lib/convert/hex.ts +++ b/src/lib/convert/hex.ts @@ -22,7 +22,7 @@ export const hex = Object.freeze({ * @param {number} [padding=1] - Minimum length of the resulting array padded as necessary with starting 0x00 bytes * @returns {Uint8Array} Byte array representation of the input value */ - toBytes (hex: string, padding: number = 1): Uint8Array { + toBytes (hex: string, padding: number = 1): Bytes { if (typeof hex !== 'string' || !/^[0-9a-f]+$/i.test(hex)) { throw new TypeError('Invalid string when converting hex to bytes', { cause: hex }) } diff --git a/src/lib/convert/utf8.ts b/src/lib/convert/utf8.ts index 1d0037f..0a9d1b1 100644 --- a/src/lib/convert/utf8.ts +++ b/src/lib/convert/utf8.ts @@ -22,7 +22,7 @@ export const utf8 = Object.freeze({ * @param {string} utf8 - String to convert * @returns {Uint8Array} Byte array representation of the input string */ - toBytes (utf8: string): Uint8Array { + toBytes (utf8: string): Bytes { return encoder.encode(utf8) }, diff --git a/src/lib/crypto/bip39.ts b/src/lib/crypto/bip39.ts index 92d2be2..c1dcd1e 100644 --- a/src/lib/crypto/bip39.ts +++ b/src/lib/crypto/bip39.ts @@ -12,10 +12,10 @@ export class Bip39 { * SHA-256 hash of entropy that is appended to the entropy and subsequently * used to generate the mnemonic phrase. * - * @param {Uint8Array} entropy - Cryptographically strong pseudorandom data of length N bits + * @param {Bytes} entropy - Cryptographically strong pseudorandom data of length N bits * @returns {Promise} First N/32 bits of the hash as a bigint */ - static checksum (entropy: Uint8Array): Promise { + static checksum (entropy: Bytes): Promise { const checksumBitLength = (entropy.byteLength / 4) | 0 return crypto.subtle.digest('SHA-256', entropy) .then(hash => { @@ -32,10 +32,10 @@ export class Bip39 { * the limit of 128-256 bits defined in BIP-39. Typically, wallets use the * maximum entropy allowed. * - * @param {(ArrayBuffer|Uint8Array)} entropy - Cryptographically secure random value + * @param {(ArrayBuffer|Bytes)} entropy - Cryptographically secure random value * @returns {Promise} Mnemonic phrase created using the BIP-39 wordlist */ - static fromEntropy (entropy: ArrayBuffer | Uint8Array): Promise { + static fromEntropy (entropy: ArrayBuffer | Bytes): Promise { if (entropy instanceof ArrayBuffer) { entropy = new Uint8Array(entropy) } @@ -144,8 +144,8 @@ export class Bip39 { private constructor () { } - #bip39Seed?: Uint8Array - #blake2bSeed?: Uint8Array + #bip39Seed?: Bytes + #blake2bSeed?: Bytes #phrase?: string[] /** @@ -172,9 +172,9 @@ export class Bip39 { * or not a string, the empty string ("") is used instead. * * @param {string} [passphrase=''] - Used as the PBKDF2 salt. Default: "" - * @returns {Promise>} Promise for seed as bytes + * @returns {Promise} Promise for seed as bytes */ - toBip39Seed (passphrase: string): Promise> + toBip39Seed (passphrase: string): Promise /** * Converts the mnemonic phrase to a BIP-39 seed. * @@ -185,7 +185,7 @@ export class Bip39 { * @returns {Promise} Promise for seed as hexadecimal string */ toBip39Seed (passphrase: string, format: 'hex'): Promise - toBip39Seed (passphrase: unknown, format?: 'hex'): Promise> { + toBip39Seed (passphrase: unknown, format?: 'hex'): Promise { if (this.phrase == null) { throw new Error('BIP-39 mnemonic phrase not found') } @@ -217,9 +217,9 @@ export class Bip39 { /** * Converts the mnemonic phrase to a BLAKE2b seed. * - * @returns {Uint8Array} Seed as bytes + * @returns {Bytes} Seed as bytes */ - toBlake2bSeed (): Uint8Array + toBlake2bSeed (): Bytes /** * Converts the mnemonic phrase to a BLAKE2b seed. * @@ -227,7 +227,7 @@ export class Bip39 { * @returns {string} Seed as hexadecimal string */ toBlake2bSeed (format: 'hex'): string - toBlake2bSeed (format?: 'hex'): string | Uint8Array { + toBlake2bSeed (format?: 'hex'): string | Bytes { if (this.#phrase?.length !== 24) { throw new Error('BIP-39 mnemonic phrase must be 24 words to convert to BLAKE2b seed') } diff --git a/src/lib/crypto/bip44.ts b/src/lib/crypto/bip44.ts index 9ad69e2..7431ebb 100644 --- a/src/lib/crypto/bip44.ts +++ b/src/lib/crypto/bip44.ts @@ -3,7 +3,6 @@ import { Point, getPublicKey as secp256k1_getPublicKey } from '@noble/secp256k1' -type Bytes = Uint8Array type Curve = 'Bitcoin seed' | 'ed25519 seed' type ExtendedKey = { privateKey: ArrayBuffer diff --git a/src/lib/crypto/blake2b.ts b/src/lib/crypto/blake2b.ts index ae0e4b9..f9a9f07 100644 --- a/src/lib/crypto/blake2b.ts +++ b/src/lib/crypto/blake2b.ts @@ -164,7 +164,7 @@ export class Blake2b { * * @param {Uint8Array} out - Buffer to store the final output */ - #blake2bFinal (out: Uint8Array): void { + #blake2bFinal (out: Bytes): void { this.#t += BigInt(this.#c) // add final message block size to total bytes this.#b.fill(0, this.#c) // pad final block with zeros this.#blake2bCompress(true) // set final block flag and compress @@ -212,11 +212,11 @@ export class Blake2b { * Creates a BLAKE2b hashing context. * * @param {number} length - Output length between 1-64 bytes - * @param {Uint8Array} [key] - (_optional_) Used for keyed hashing (MAC and PRF) - * @param {Uint8Array} [salt] - (_optional_) Used to simplify randomized hashing for digital signatures - * @param {Uint8Array} [personal] - (_optional_) Arbitrary user-specified value + * @param {Bytes} [key] - (_optional_) Used for keyed hashing (MAC and PRF) + * @param {Bytes} [salt] - (_optional_) Used to simplify randomized hashing for digital signatures + * @param {Bytes} [personal] - (_optional_) Arbitrary user-specified value */ - constructor (length: number, key?: Uint8Array, salt?: Uint8Array, personal?: Uint8Array) + constructor (length: number, key?: Bytes, salt?: Bytes, personal?: Bytes) constructor (length: unknown, key?: unknown, salt?: unknown, personal?: unknown) { const B = this.constructor as typeof Blake2b if (length == null) { @@ -298,9 +298,9 @@ export class Blake2b { * Finalizes the streaming hash with one last compression and truncates the * output to the requested byte length. * - * @returns {Uint8Array} + * @returns {Bytes} */ - digest (): Uint8Array { + digest (): Bytes { const buf = new Uint8Array(this.#outlen) this.#blake2bFinal(buf) return buf diff --git a/src/lib/ledger/sign.ts b/src/lib/ledger/sign.ts index 0bf5de7..8c8a553 100644 --- a/src/lib/ledger/sign.ts +++ b/src/lib/ledger/sign.ts @@ -1,7 +1,7 @@ //! SPDX-FileCopyrightText: 2025 Chris Duncan //! SPDX-License-Identifier: GPL-3.0-or-later -import { APDU_CODES, DERIVATION_PATH, LedgerTransport, LISTEN_TIMEOUT, OPEN_TIMEOUT, STATUS_CODES } from '.' +import { APDU_CODES, DERIVATION_PATH, LISTEN_TIMEOUT, LedgerTransport, OPEN_TIMEOUT, STATUS_CODES } from '.' import { Account } from '../account' import { Block } from '../block' import { HARDENED_OFFSET } from '../constants' @@ -60,7 +60,7 @@ async function signBlock (transport: LedgerTransport, index: number, block: Bloc return res.signature } -async function signNonce (transport: LedgerTransport, index: number, nonce: Uint8Array): Promise { +async function signNonce (transport: LedgerTransport, index: number, nonce: Bytes): Promise { if (nonce.byteLength !== 16) { throw new RangeError('Nonce must be 16-byte string') } diff --git a/src/lib/tools.ts b/src/lib/tools.ts index 24a5e90..d88a34f 100644 --- a/src/lib/tools.ts +++ b/src/lib/tools.ts @@ -16,7 +16,7 @@ type SweepResult = { } export class Tools { - static #normalize (input: string | ArrayBuffer | Uint8Array): Uint8Array { + static #normalize (input: string | ArrayBuffer | Bytes): Bytes { return (typeof input === 'string') ? hex.toBytes(input) : input instanceof ArrayBuffer @@ -112,11 +112,11 @@ export class Tools { * Signs an arbitrary string with a secret key using nano25519. The input data * is encoded as UTF-8 and can be up to 32 KiB in total. * - * @param {(string | ArrayBuffer | Uint8Array)} secretKey - 64-byte secret key + * @param {(string | ArrayBuffer | Bytes)} secretKey - 64-byte secret key * @param {string} input - Data to be signed * @returns {string} 64-byte hexadecimal signature */ - static sign (secretKey: string | ArrayBuffer | Uint8Array, input: string): string { + static sign (secretKey: string | ArrayBuffer | Bytes, input: string): string { if (navigator.userActivation?.isActive === false) { throw new DOMException( 'Signing request was blocked due to lack of user activation', @@ -195,12 +195,12 @@ export class Tools { /** * Verifies the signature of an arbitrary string using a public key. * - * @param {(string | ArrayBuffer | Uint8Array)} publicKey - 32-byte hexadecimal public key - * @param {(string | ArrayBuffer | Uint8Array)} signature - 128-character hexadcimal signature + * @param {(string | ArrayBuffer | Bytes)} publicKey - 32-byte hexadecimal public key + * @param {(string | ArrayBuffer | Bytes)} signature - 128-character hexadcimal signature * @param {string} input - Data to be verified * @returns {boolean} True if the data was signed by the public key's matching private key */ - static verify (publicKey: string | ArrayBuffer | Uint8Array, signature: string | ArrayBuffer | Uint8Array, input: string): boolean { + static verify (publicKey: string | ArrayBuffer | Bytes, signature: string | ArrayBuffer | Bytes, input: string): boolean { const k = this.#normalize(publicKey) const s = this.#normalize(signature) try { diff --git a/src/lib/vault/vault-worker.ts b/src/lib/vault/vault-worker.ts index 34e3334..4c53ed8 100644 --- a/src/lib/vault/vault-worker.ts +++ b/src/lib/vault/vault-worker.ts @@ -11,7 +11,7 @@ import { Passkey } from './passkey' import { VaultTimer } from './vault-timer' const encoder = new TextEncoder() -const encode = (input?: string): Uint8Array => encoder.encode(input) +const encode = (input?: string): Bytes => encoder.encode(input) let _locked: boolean = true let _timeout: number = 120_000 let _timer: VaultTimer = new VaultTimer(() => { }, 0) -- 2.52.0