//! 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
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
* 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
* @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
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
* 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
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
//! 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'
/**
* 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)
/**
* 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)
* 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.
[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>
}
/**
/**
* 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.
/**
* 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