]> git.codecow.com Git - libnemo.git/commitdiff
Promote Bytes type alias from BIP-44 to global scope.
authorChris Duncan <chris@codecow.com>
Wed, 1 Jul 2026 05:30:47 +0000 (22:30 -0700)
committerChris Duncan <chris@codecow.com>
Wed, 1 Jul 2026 05:30:47 +0000 (22:30 -0700)
15 files changed:
src/index.ts
src/lib/account/address.ts
src/lib/account/index.ts
src/lib/block/index.ts
src/lib/convert/base32.ts
src/lib/convert/bytes.ts
src/lib/convert/dec.ts
src/lib/convert/hex.ts
src/lib/convert/utf8.ts
src/lib/crypto/bip39.ts
src/lib/crypto/bip44.ts
src/lib/crypto/blake2b.ts
src/lib/ledger/sign.ts
src/lib/tools.ts
src/lib/vault/vault-worker.ts

index 985f6bea36a32e1c98924b9c433c8fa87b811a69..641c1e732c4bda39255d013ccb637e5f09635395 100644 (file)
@@ -10,6 +10,10 @@ import { Rpc } from './lib/rpc'
 import { Tools } from './lib/tools'
 import { Wallet } from './lib/wallet'
 
+declare global {
+       type Bytes = Uint8Array<ArrayBuffer>
+}
+
 export {
        Account,
        Blake2b,
index 5995847ff47f0fb75332f470a2dfa11528bd4313..83bfd27e5e9e98a315dc449aa91d558dcf7257b6 100644 (file)
@@ -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<ArrayBuffer>): 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<ArrayBuffer>)
+       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<ArrayBuffer> {
+       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()
index 75a182241a9ecee091945fec59e6f50aed4f8a05..ef1230458e6357fae98531027bdaccfb1676a0f9 100644 (file)
@@ -22,7 +22,7 @@ export class Account {
        /** @returns {'Account'} */\r
        static get DB_NAME (): 'Account' { return 'Account' }\r
 \r
-       #publicKey: Uint8Array<ArrayBuffer> = new Uint8Array(32)\r
+       #publicKey: Bytes = new Uint8Array(32)\r
        #address?: Address\r
 \r
        #confirmed_balance?: bigint\r
@@ -157,21 +157,21 @@ export class Account {
        constructor (address: string)\r
        /**\r
         * Instantiates an Account object from its public key.\r
-        * @param {(string | Uint8Array<ArrayBuffer>)} publicKey - Public key of the account\r
+        * @param {(string | Bytes)} publicKey - Public key of the account\r
         * @returns {Account} A new Account object\r
         */\r
-       constructor (publicKey: string | ArrayBuffer | Uint8Array<ArrayBuffer>)\r
+       constructor (publicKey: string | ArrayBuffer | Bytes)\r
        /**\r
         * Instantiates an Account object from its public or private key.\r
         *\r
         * If the key is indicated as private, then it is copied locally, used to\r
         * derive the corresponding public key, and finally zeroed out; the user is\r
         * responsible for securely handling the original input.\r
-        * @param {(string | Uint8Array<ArrayBuffer>)} key - Public or private key of the account\r
+        * @param {(string | Bytes)} key - Public or private key of the account\r
         * @param {string} type - Indicates which type the key is\r
         * @returns {Account} A new Account object\r
         */\r
-       constructor (key: string | ArrayBuffer | Uint8Array<ArrayBuffer>, type: 'public' | 'private')\r
+       constructor (key: string | ArrayBuffer | Bytes, type: 'public' | 'private')\r
        constructor (input: unknown, type: unknown = 'public') {\r
                if (type === 'private') {\r
                        try {\r
index 7e0adad475fbc2759980e26a482017728765030e..9f348a2e2cbc72affc3f6b37a37dc4367b7fc968 100644 (file)
@@ -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<ArrayBuffer> | '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<ArrayBuffer>
+       previous: Bytes
        representative?: Account
-       link?: Uint8Array<ArrayBuffer>
+       link?: Bytes
        signature?: string
        work?: string
 
index 0896f39ef15722baa77a31aafaac6a35c7367ccd..008366bb2e96c6b10b8efd9edc6ed0c184549c81 100644 (file)
@@ -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<ArrayBuffer> {
+       toBytes (base32: string): Bytes {
                const leftover = (base32.length * 5) % 8
                const offset = leftover === 0
                        ? 0
index af27d45cc44b14e215f0b05ac8e7c578c9017bd2..acb6031c36df5034c9645d0d094cbd24d497da39 100644 (file)
@@ -12,7 +12,7 @@ export const bytes = Object.freeze({
         *
         * @param bytes - Buffer or bytes to erase
         */
-       erase (bytes?: ArrayBuffer | Uint8Array<ArrayBuffer> | 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<ArrayBuffer>): string {
+       toUtf8 (bytes: Bytes): string {
                return decoder.decode(bytes)
        },
 })
index 5f8f4fe59f9ce7bc5704edd4225c6a109089e24a..b69979b11aca21605db369ecf2f94f3e4bffa3c4 100644 (file)
@@ -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<ArrayBuffer> {
+       toBytes (decimal: bigint | number | string, padding: number = 1): Bytes {
                if (decimal == null) {
                        throw new TypeError(`Failed to convert '${decimal}' from decimal to bytes`)
                }
index fb4360ecef4e0844d6c01364748044eb57c16221..f37d4debd058518b35f4ceb9ca9506541a32cf55 100644 (file)
@@ -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<ArrayBuffer> {
+       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 })
                }
index 1d0037fabd3539be1edfe2897c83b62c6c614dfb..0a9d1b1cb341703ed6574f06c765b92160feccc7 100644 (file)
@@ -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<ArrayBuffer> {
+       toBytes (utf8: string): Bytes {
                return encoder.encode(utf8)
        },
 
index 92d2be2b9ef148eadff469d274c741f8b45f04b2..c1dcd1e40dd0d75fb8063806acbc60bd76084ba7 100644 (file)
@@ -12,10 +12,10 @@ export class Bip39 {
         * SHA-256 hash of entropy that is appended to the entropy and subsequently\r
         * used to generate the mnemonic phrase.\r
         *\r
-        * @param {Uint8Array<ArrayBuffer>} entropy - Cryptographically strong pseudorandom data of length N bits\r
+        * @param {Bytes} entropy - Cryptographically strong pseudorandom data of length N bits\r
         * @returns {Promise<bigint>} First N/32 bits of the hash as a bigint\r
         */\r
-       static checksum (entropy: Uint8Array<ArrayBuffer>): Promise<bigint> {\r
+       static checksum (entropy: Bytes): Promise<bigint> {\r
                const checksumBitLength = (entropy.byteLength / 4) | 0\r
                return crypto.subtle.digest('SHA-256', entropy)\r
                        .then(hash => {\r
@@ -32,10 +32,10 @@ export class Bip39 {
        * the limit of 128-256 bits defined in BIP-39. Typically, wallets use the\r
        * maximum entropy allowed.\r
        *\r
-       * @param {(ArrayBuffer|Uint8Array<ArrayBuffer>)} entropy - Cryptographically secure random value\r
+       * @param {(ArrayBuffer|Bytes)} entropy - Cryptographically secure random value\r
        * @returns {Promise<Bip39>} Mnemonic phrase created using the BIP-39 wordlist\r
        */\r
-       static fromEntropy (entropy: ArrayBuffer | Uint8Array<ArrayBuffer>): Promise<Bip39> {\r
+       static fromEntropy (entropy: ArrayBuffer | Bytes): Promise<Bip39> {\r
                if (entropy instanceof ArrayBuffer) {\r
                        entropy = new Uint8Array(entropy)\r
                }\r
@@ -144,8 +144,8 @@ export class Bip39 {
 \r
        private constructor () { }\r
 \r
-       #bip39Seed?: Uint8Array<ArrayBuffer>\r
-       #blake2bSeed?: Uint8Array<ArrayBuffer>\r
+       #bip39Seed?: Bytes\r
+       #blake2bSeed?: Bytes\r
        #phrase?: string[]\r
 \r
        /**\r
@@ -172,9 +172,9 @@ export class Bip39 {
        * or not a string, the empty string ("") is used instead.\r
        *\r
        * @param {string} [passphrase=''] - Used as the PBKDF2 salt. Default: ""\r
-       * @returns {Promise<Uint8Array<ArrayBuffer>>} Promise for seed as bytes\r
+       * @returns {Promise<Bytes>} Promise for seed as bytes\r
        */\r
-       toBip39Seed (passphrase: string): Promise<Uint8Array<ArrayBuffer>>\r
+       toBip39Seed (passphrase: string): Promise<Bytes>\r
        /**\r
        * Converts the mnemonic phrase to a BIP-39 seed.\r
        *\r
@@ -185,7 +185,7 @@ export class Bip39 {
        * @returns {Promise<string>}  Promise for seed as hexadecimal string\r
        */\r
        toBip39Seed (passphrase: string, format: 'hex'): Promise<string>\r
-       toBip39Seed (passphrase: unknown, format?: 'hex'): Promise<string | Uint8Array<ArrayBuffer>> {\r
+       toBip39Seed (passphrase: unknown, format?: 'hex'): Promise<string | Bytes> {\r
                if (this.phrase == null) {\r
                        throw new Error('BIP-39 mnemonic phrase not found')\r
                }\r
@@ -217,9 +217,9 @@ export class Bip39 {
        /**\r
        * Converts the mnemonic phrase to a BLAKE2b seed.\r
        *\r
-       * @returns {Uint8Array<ArrayBuffer>} Seed as bytes\r
+       * @returns {Bytes} Seed as bytes\r
        */\r
-       toBlake2bSeed (): Uint8Array<ArrayBuffer>\r
+       toBlake2bSeed (): Bytes\r
        /**\r
        * Converts the mnemonic phrase to a BLAKE2b seed.\r
        *\r
@@ -227,7 +227,7 @@ export class Bip39 {
        * @returns {string} Seed as hexadecimal string\r
        */\r
        toBlake2bSeed (format: 'hex'): string\r
-       toBlake2bSeed (format?: 'hex'): string | Uint8Array<ArrayBuffer> {\r
+       toBlake2bSeed (format?: 'hex'): string | Bytes {\r
                if (this.#phrase?.length !== 24) {\r
                        throw new Error('BIP-39 mnemonic phrase must be 24 words to convert to BLAKE2b seed')\r
                }\r
index 9ad69e29e32b5647855a5750f26667a7663d484c..7431ebbe2cab1aa3b58f0e52c0a822e23e844910 100644 (file)
@@ -3,7 +3,6 @@
 
 import { Point, getPublicKey as secp256k1_getPublicKey } from '@noble/secp256k1'
 
-type Bytes = Uint8Array<ArrayBuffer>
 type Curve = 'Bitcoin seed' | 'ed25519 seed'
 type ExtendedKey = {
        privateKey: ArrayBuffer
index ae0e4b99136bd3ef48e186e3b82fc7ae6501b1a0..f9a9f074c891f0cdde968764217202a4a51ee8c6 100644 (file)
@@ -164,7 +164,7 @@ export class Blake2b {
         *
         * @param {Uint8Array} out - Buffer to store the final output
         */
-       #blake2bFinal (out: Uint8Array<ArrayBuffer>): 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<ArrayBuffer>, salt?: Uint8Array<ArrayBuffer>, personal?: Uint8Array<ArrayBuffer>)
+       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<ArrayBuffer>}
+        * @returns {Bytes}
         */
-       digest (): Uint8Array<ArrayBuffer> {
+       digest (): Bytes {
                const buf = new Uint8Array(this.#outlen)
                this.#blake2bFinal(buf)
                return buf
index 0bf5de70f94b33e237efdbcb5d5b3b04e5c9fe9d..8c8a553f344d761e1240cd842b9ce71afc8e4833 100644 (file)
@@ -1,7 +1,7 @@
 //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
 //! 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<ArrayBuffer>): Promise<string> {
+async function signNonce (transport: LedgerTransport, index: number, nonce: Bytes): Promise<string> {
        if (nonce.byteLength !== 16) {
                throw new RangeError('Nonce must be 16-byte string')
        }
index 24a5e902b259d7d04fe0483204f714b71ae3df48..d88a34f1857e502190e23af3d0751b64f6ab5373 100644 (file)
@@ -16,7 +16,7 @@ type SweepResult = {
 }
 
 export class Tools {
-       static #normalize (input: string | ArrayBuffer | Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer> {
+       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<ArrayBuffer>)} 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<ArrayBuffer>, 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<ArrayBuffer>)} publicKey - 32-byte hexadecimal public key
-        * @param {(string | ArrayBuffer | Uint8Array<ArrayBuffer>)} 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<ArrayBuffer>, signature: string | ArrayBuffer | Uint8Array<ArrayBuffer>, 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 {
index 34e333466da815788403ba621b67f2e708101878..4c53ed8ac1c9489781f27f7563f921997be853a1 100644 (file)
@@ -11,7 +11,7 @@ import { Passkey } from './passkey'
 import { VaultTimer } from './vault-timer'
 
 const encoder = new TextEncoder()
-const encode = (input?: string): Uint8Array<ArrayBuffer> => 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)