]> git.codecow.com Git - libnemo.git/commitdiff
Deprecate unnecessary Key type alias. Inline public and private key validation.
authorChris Duncan <chris@zoso.dev>
Fri, 22 Aug 2025 07:02:28 +0000 (00:02 -0700)
committerChris Duncan <chris@zoso.dev>
Fri, 22 Aug 2025 07:02:28 +0000 (00:02 -0700)
src/lib/account/index.ts
src/lib/tools.ts
src/types.d.ts

index 5954e0240cc6d0fdc265c23d9d3fe7a4a668db56..b957c11204f65b64b88102fa30e541ba69794f40 100644 (file)
@@ -1,7 +1,7 @@
 //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>\r
 //! SPDX-License-Identifier: GPL-3.0-or-later\r
 \r
-import { Key, KeyPair } from '#types'\r
+import { KeyPair } from '#types'\r
 import { Block } from '../block'\r
 import { ACCOUNT_KEY_BYTE_LENGTH, ACCOUNT_KEY_HEX_LENGTH, ALPHABET, PREFIX, PREFIX_LEGACY } from '../constants'\r
 import { base32, bytes, hex } from '../convert'\r
@@ -97,7 +97,7 @@ export class Account {
        set representative_block (v: string | undefined) { this.#representative_block = v }\r
        set weight (v: bigint | number | string) { this.#weight = BigInt(v) }\r
 \r
-       private constructor (address: string, publicKey: Key, index?: number) {\r
+       private constructor (address: string, publicKey: string | Uint8Array<ArrayBuffer>, index?: number) {\r
                if (!Account.#isInternal) {\r
                        throw new Error('Account cannot be instantiated directly. Use `load()` instead.')\r
                }\r
@@ -155,18 +155,18 @@ export class Account {
        * Instantiates an Account object from its public key. It is unable to sign\r
        * blocks or messages since it has no private key.\r
        *\r
-       * @param {Key} publicKey - Public key of the account\r
+       * @param {string | Uint8Array<ArrayBuffer>} publicKey - Public key of the account\r
        * @returns {Account} A new Account object\r
        */\r
-       static load (publicKey: Key): Account\r
+       static load (publicKey: string | Uint8Array<ArrayBuffer>): Account\r
        /**\r
        * Instantiates Account objects from their public keys. They are unable to sign\r
        * blocks or messages since they have no private key.\r
        *\r
-       * @param {Key[]} publicKeys - Public keys of the accounts\r
+       * @param {string | Uint8Array<ArrayBuffer>[]} publicKeys - Public keys of the accounts\r
        * @returns {Account[]} Array of new Account objects\r
        */\r
-       static load (publicKeys: Key[]): Account[]\r
+       static load (publicKeys: string | Uint8Array<ArrayBuffer>[]): Account[]\r
        /**\r
        * Instantiates an Account object from its public key. It is unable to sign\r
        * blocks or messages since it has no private key.\r
@@ -201,7 +201,7 @@ export class Account {
        * @returns {Promise<Account[]>} Promise for array of new Account objects\r
        */\r
        static async load (keypairs: KeyPair[], type: 'private'): Promise<Account[]>\r
-       static load (input: Key | KeyPair | (Key | KeyPair)[], type?: 'private'): Account | Account[] | Promise<Account | Account[]> {\r
+       static load (input: string | Uint8Array<ArrayBuffer> | KeyPair | (string | Uint8Array<ArrayBuffer> | KeyPair)[], type?: 'private'): Account | Account[] | Promise<Account | Account[]> {\r
                const isInputArray = Array.isArray(input)\r
                const inputs = isInputArray ? input : [input]\r
                if (this.#isKeyPairs(inputs) && type === 'private') {\r
@@ -269,9 +269,15 @@ export class Account {
                                if (index == null) {\r
                                        throw new RangeError('Index missing for Account')\r
                                }\r
-                               this.#validateKey(privateKey)\r
-                               if (typeof privateKey === 'string') privateKey = hex.toBytes(privateKey)\r
-\r
+                               if (typeof privateKey === 'string' && RegExp(`^[A-F0-9]{${ACCOUNT_KEY_HEX_LENGTH}}$`, 'i').test(privateKey)) {\r
+                                       privateKey = hex.toBytes(privateKey)\r
+                               }\r
+                               if (!(privateKey instanceof Uint8Array) || privateKey.every(v => v === 0)) {\r
+                                       throw new TypeError('Invalid private key')\r
+                               }\r
+                               if (privateKey.byteLength !== ACCOUNT_KEY_BYTE_LENGTH) {\r
+                                       throw new TypeError(`Private key must be ${ACCOUNT_KEY_BYTE_LENGTH} bytes`)\r
+                               }\r
                                const publicKey = await NanoNaCl.convert(privateKey)\r
                                const address = this.#keyToAddress(publicKey)\r
                                this.#isInternal = true\r
@@ -287,59 +293,62 @@ export class Account {
        * Instantiates Account objects from public data, each specifying either its\r
        * public key or its Nano address.\r
        *\r
-       * @param {Key[]} input - Public keys or addresses of the accounts\r
+       * @param {(string | Uint8Array<ArrayBuffer>|KeyPair)[]} input - Public keys or addresses of the accounts\r
        * @returns {Account[]} The instantiated Account objects\r
        */\r
-       static #fromPublic (input: (Key | KeyPair)[] | unknown): Account[] {\r
-               const keypairs = this.#isKeyPairs(input)\r
-                       ? input\r
-                       : this.#isKeys(input)\r
-                               ? input.map(i => { return { publicKey: i } as KeyPair })\r
-                               : []\r
-               if (keypairs.length === 0) {\r
-                       throw new TypeError('Invalid public input for Account')\r
-               }\r
+       static #fromPublic (input: (string | Uint8Array<ArrayBuffer> | KeyPair)[] | unknown): Account[] {\r
+               try {\r
+                       const keypairs = this.#isKeyPairs(input)\r
+                               ? input\r
+                               : this.#isKeys(input)\r
+                                       ? input.map(i => { return { publicKey: i } as KeyPair })\r
+                                       : []\r
+                       if (keypairs.length === 0) {\r
+                               throw new TypeError('Invalid public input for Account')\r
+                       }\r
 \r
-               const accounts: Account[] = []\r
-               let address: string\r
-               let publicKey: Uint8Array<ArrayBuffer>\r
-               let index\r
-               for (let keypair of keypairs) {\r
-                       let keyError, addressError\r
-                       const key = keypair.publicKey instanceof ArrayBuffer\r
-                               ? new Uint8Array(keypair.publicKey)\r
-                               : typeof keypair.publicKey === 'string' && [PREFIX, PREFIX_LEGACY].includes(keypair.publicKey.slice(0, 5))\r
-                                       ? this.#addressToKey(keypair.publicKey)\r
-                                       : keypair.publicKey\r
-                       try {\r
-                               this.#validateKey(key)\r
-                               publicKey = (typeof key === 'string')\r
-                                       ? hex.toBytes(key)\r
-                                       : key\r
-                               address = this.#keyToAddress(publicKey)\r
-                       } catch (err) {\r
-                               keyError = err\r
-                               try {\r
-                                       this.validate(key)\r
-                                       address = key\r
-                                       publicKey = this.#addressToKey(address)\r
-                               } catch (err) {\r
-                                       addressError = err\r
-                                       throw new TypeError('Failed to import Account from public data', { cause: { keyError, addressError } })\r
+                       const accounts: Account[] = []\r
+                       for (let keypair of keypairs) {\r
+                               const { index } = keypair\r
+                               if (typeof keypair.publicKey === 'string') {\r
+                                       if (RegExp(`^[A-F0-9]{${ACCOUNT_KEY_HEX_LENGTH}}$`, 'i').test(keypair.publicKey)) {\r
+                                               const publicKey = hex.toBytes(keypair.publicKey)\r
+                                               const address = this.#keyToAddress(publicKey)\r
+                                               this.#isInternal = true\r
+                                               accounts.push(new this(address, publicKey, index))\r
+                                       } else if (RegExp(`(${PREFIX}|${PREFIX_LEGACY})`).test(keypair.publicKey)) {\r
+                                               const address = keypair.publicKey\r
+                                               const publicKey = this.#addressToKey(address)\r
+                                               this.#isInternal = true\r
+                                               accounts.push(new this(address, publicKey, index))\r
+                                       } else {\r
+                                               throw new TypeError('Invalid string', { cause: keypair.publicKey })\r
+                                       }\r
+                               } else if (keypair.publicKey instanceof ArrayBuffer) {\r
+                                       if (keypair.publicKey.byteLength === ACCOUNT_KEY_BYTE_LENGTH) {\r
+                                               const publicKey = new Uint8Array(keypair.publicKey)\r
+                                               const address = this.#keyToAddress(publicKey)\r
+                                               this.#isInternal = true\r
+                                               accounts.push(new this(address, publicKey, index))\r
+                                       } else {\r
+                                               throw new TypeError('Invalid buffer', { cause: keypair.publicKey })\r
+                                       }\r
+                               } else {\r
+                                       throw new TypeError('Invalid Account input', { cause: keypair.publicKey })\r
                                }\r
                        }\r
-                       index = keypair.index\r
-                       this.#isInternal = true\r
-                       accounts.push(new this(address, publicKey, index))\r
+                       return accounts\r
+               } catch (err) {\r
+                       console.error(err)\r
+                       throw new TypeError('Failed to import Account from public data', { cause: { err } })\r
                }\r
-               return accounts\r
        }\r
 \r
-       static #isKey (input: unknown): input is Key {\r
+       static #isKey (input: unknown): input is string | Uint8Array<ArrayBuffer> {\r
                return typeof input === 'string' || (input instanceof Uint8Array && 'buffer' in input)\r
        }\r
 \r
-       static #isKeys (input: unknown): input is Key[] {\r
+       static #isKeys (input: unknown): input is (string | Uint8Array<ArrayBuffer>)[] {\r
                if (Array.isArray(input)) {\r
                        for (const i of input) {\r
                                if (!this.#isKey(i)) {\r
@@ -389,40 +398,6 @@ export class Account {
                const encodedChecksum = bytes.toBase32(checksum)\r
                return `${PREFIX}${encodedPublicKey}${encodedChecksum}`\r
        }\r
-\r
-       /**\r
-       * Validates a public or private key is 32-byte array or a 64-char hex string.\r
-       *\r
-       * @param {unknown} key - Key bytes as Uint8Array or hexadecimal string\r
-       * @throws If key is invalid\r
-       */\r
-       static #validateKey (key: unknown): asserts key is (Key) {\r
-               if (key === undefined) {\r
-                       throw new TypeError(`Key is undefined`)\r
-               }\r
-               if (key instanceof ArrayBuffer) {\r
-                       key = new Uint8Array(key)\r
-               }\r
-               if (typeof key !== 'string' && !(key instanceof Uint8Array)) {\r
-                       throw new TypeError(`Key must be a string or Uint8Array`)\r
-               }\r
-               if (typeof key === 'string') {\r
-                       if (key.length !== ACCOUNT_KEY_HEX_LENGTH) {\r
-                               throw new TypeError(`Key must be ${ACCOUNT_KEY_HEX_LENGTH} characters`)\r
-                       }\r
-                       if (!/^[A-Fa-f0-9]{64}$/i.test(key)) {\r
-                               throw new RangeError(`Key is not a valid hexadecimal value`)\r
-                       }\r
-               }\r
-               if (key instanceof Uint8Array) {\r
-                       if (key.byteLength !== ACCOUNT_KEY_BYTE_LENGTH) {\r
-                               throw new TypeError(`Key must be ${ACCOUNT_KEY_BYTE_LENGTH} BYTES`)\r
-                       }\r
-                       if (key.every(v => v === 0)) {\r
-                               throw new TypeError(`Key is not a valid byte array`)\r
-                       }\r
-               }\r
-       }\r
 }\r
 \r
 export class AccountList extends Object {\r
index 6e753bebf93b98841439e385a7c61f5a12817fac..e279c57d2bec6c6c6bef2acf05bfb3dda88857f1 100644 (file)
@@ -1,7 +1,7 @@
 //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
 //! SPDX-License-Identifier: GPL-3.0-or-later
 
-import { Key, SweepResult } from '#types'
+import { SweepResult } from '#types'
 import { Account } from './account'
 import { Block } from './block'
 import { MAX_SUPPLY, UNITS } from './constants'
@@ -96,11 +96,11 @@ function hash (data: string | string[], encoding?: 'hex', format?: 'hex'): strin
 /**
 * Signs arbitrary strings with a private key using the Ed25519 signature scheme.
 *
-* @param {Key} key - Hexadecimal-formatted private key to use for signing
+* @param {string | Uint8Array<ArrayBuffer>} key - Hexadecimal-formatted private key to use for signing
 * @param {...string} input - Data to be signed
 * @returns {Promise<string>} Hexadecimal-formatted signature
 */
-export async function sign (key: Key, ...input: string[]): Promise<string> {
+export async function sign (key: string | Uint8Array<ArrayBuffer>, ...input: string[]): Promise<string> {
        if (typeof key === 'string') key = hex.toBytes(key)
        try {
                const signature = await NanoNaCl.detached(hash(input), key)
@@ -173,12 +173,12 @@ export async function sweep (
 /**
 * Verifies the signature of arbitrary strings using a public key.
 *
-* @param {Key} key - Hexadecimal-formatted public key to use for verification
+* @param {string | Uint8Array<ArrayBuffer>} key - Hexadecimal-formatted public key to use for verification
 * @param {string} signature - Hexadcimal-formatted signature
 * @param {...string} input - Data to be verified
 * @returns {Promise<boolean>} True if the data was signed by the public key's matching private key
 */
-export async function verify (key: Key, signature: string, ...input: string[]): Promise<boolean> {
+export async function verify (key: string | Uint8Array<ArrayBuffer>, signature: string, ...input: string[]): Promise<boolean> {
        if (typeof key === 'string') key = hex.toBytes(key)
        try {
                return await NanoNaCl.verify(hash(input), hex.toBytes(signature), key)
index 8cf9870598bf356ba5db61994b0e85cbfb941c97..6062b468ff59802fca4b4013875438aeb09e36e6 100644 (file)
@@ -70,18 +70,18 @@ export declare class Account {
        * Instantiates an Account object from its public key. It is unable to sign
        * blocks or messages since it has no private key.
        *
-       * @param {Key} publicKey - Public key of the account
+       * @param {string | Uint8Array<ArrayBuffer>} publicKey - Public key of the account
        * @returns {Account} A new Account object
        */
-       static load (publicKey: Key): Account
+       static load (publicKey: string | Uint8Array<ArrayBuffer>): Account
        /**
        * Instantiates Account objects from their public keys. They are unable to sign
        * blocks or messages since they have no private key.
        *
-       * @param {Key[]} publicKeys - Public keys of the accounts
+       * @param {string | Uint8Array<ArrayBuffer>[]} publicKeys - Public keys of the accounts
        * @returns {Account[]} Array of new Account objects
        */
-       static load (publicKeys: Key[]): Account[]
+       static load (publicKeys: string | Uint8Array<ArrayBuffer>[]): Account[]
        /**
        * Instantiates an Account object from its public key. It is unable to sign
        * blocks or messages since it has no private key.
@@ -389,12 +389,10 @@ export type NamedData<T extends Data = Data> = {
        [key: string]: T
 }
 
-export type Key = string | Uint8Array<ArrayBuffer>
-
 export type KeyPair = {
        index?: number
-       privateKey?: Key
-       publicKey?: Key
+       privateKey?: string | Uint8Array<ArrayBuffer>
+       publicKey?: string | Uint8Array<ArrayBuffer>
 }
 
 /**
@@ -501,11 +499,11 @@ declare function hash (data: string | string[], encoding?: 'hex'): Uint8Array<Ar
 /**
 * Signs arbitrary strings with a private key using the Ed25519 signature scheme.
 *
-* @param {Key} key - Hexadecimal-formatted private key to use for signing
+* @param {string | Uint8Array<ArrayBuffer>} key - Hexadecimal-formatted private key to use for signing
 * @param {...string} input - Data to be signed
 * @returns {Promise<string>} Hexadecimal-formatted signature
 */
-export declare function sign (key: Key, ...input: string[]): Promise<string>
+export declare function sign (key: string | Uint8Array<ArrayBuffer>, ...input: string[]): Promise<string>
 /**
 * Collects the funds from a specified range of accounts in a wallet and sends
 * them all to a single recipient address. Hardware wallets are unsupported.
@@ -521,12 +519,12 @@ export declare function sweep (rpc: Rpc | string | URL, wallet: Wallet, recipien
 /**
 * Verifies the signature of arbitrary strings using a public key.
 *
-* @param {Key} key - Hexadecimal-formatted public key to use for verification
+* @param {string | Uint8Array<ArrayBuffer>} key - Hexadecimal-formatted public key to use for verification
 * @param {string} signature - Hexadcimal-formatted signature
 * @param {...string} input - Data to be verified
 * @returns {Promise<boolean>} True if the data was signed by the public key's matching private key
 */
-export declare function verify (key: Key, signature: string, ...input: string[]): Promise<boolean>
+export declare function verify (key: string | Uint8Array<ArrayBuffer>, signature: string, ...input: string[]): Promise<boolean>
 export declare const Tools: {
        convert: typeof convert
        hash: typeof hash