import { NanoNaCl } from '../crypto'\r
import { Rpc } from '../rpc'\r
import { Address } from './address'\r
+import { _load } from './load'\r
import { _refresh } from './refresh'\r
import { _validate } from './validate'\r
\r
-type KeyPair = {\r
+export type KeyPair = {\r
index?: number\r
privateKey?: string | Uint8Array<ArrayBuffer>\r
publicKey?: string | Uint8Array<ArrayBuffer>\r
* @returns {(Account | Account[] | Promise<Account | Account[]>)} Promise for array of new Account objects\r
*/\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
- return this.#fromPrivate(inputs)\r
- .then(r => isInputArray ? r : r[0])\r
- } else {\r
- return isInputArray ? this.#fromPublic(inputs) : this.#fromPublic(inputs)[0]\r
- }\r
+ return _load(input, type)\r
}\r
\r
/**\r
static validate (address: string): asserts address is string {\r
return _validate(address)\r
}\r
-\r
- /**\r
- * Instantiates an Account object from its private key which is used to derive\r
- * the corresponding public key and then discarded.\r
- *\r
- * @param {KeyPair} keypairs - Indexes and keys of the accounts\r
- * @returns {Promise<Account[]>} Promise for new Account objects\r
- */\r
- static async #fromPrivate (keypairs: KeyPair[]): Promise<Account[]> {\r
- try {\r
- const accounts: Account[] = []\r
- for (let keypair of keypairs) {\r
- let { index, privateKey } = keypair\r
- if (index == null) {\r
- throw new RangeError('Index missing for Account')\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 = new Address(publicKey)\r
- this.#isInternal = true\r
- const account = new this(address, publicKey, index)\r
- this.#isInternal = false\r
- accounts.push(account)\r
- }\r
- return accounts\r
- } catch (err) {\r
- throw new Error('Failed to import Accounts from private keys', { cause: err })\r
- }\r
- }\r
-\r
- /**\r
- * Instantiates Account objects from public data, each specifying either its\r
- * public key or its Nano address.\r
- *\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: (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
- for (let keypair of keypairs) {\r
- if (keypair.publicKey == null) {\r
- throw new TypeError('Account address or public key is required', { cause: keypair.publicKey })\r
- }\r
- const { index } = keypair\r
- const address = new Address(keypair.publicKey)\r
- const publicKey = address.toPublicKey()\r
- this.#isInternal = true\r
- const account = new this(address, publicKey, index)\r
- this.#isInternal = false\r
- accounts.push(account)\r
- }\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
- }\r
-\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 (string | Uint8Array<ArrayBuffer>)[] {\r
- if (Array.isArray(input)) {\r
- for (const i of input) {\r
- if (!this.#isKey(i)) {\r
- return false\r
- }\r
- }\r
- }\r
- return true\r
- }\r
-\r
- static #isKeyPair (input: unknown): input is KeyPair {\r
- if (typeof input === 'object') {\r
- const obj = input as { [key: string]: unknown }\r
- if ('index' in obj && typeof obj.index === 'number') {\r
- return true\r
- }\r
- if ('publicKey' in obj && this.#isKey(obj.publicKey)) {\r
- return true\r
- }\r
- if ('privateKey' in obj && this.#isKey(obj.privateKey)) {\r
- return true\r
- }\r
- }\r
- return false\r
- }\r
-\r
- static #isKeyPairs (input: unknown): input is KeyPair[] {\r
- if (Array.isArray(input)) {\r
- for (const i of input) {\r
- if (!this.#isKeyPair(i)) {\r
- return false\r
- }\r
- }\r
- }\r
- return true\r
- }\r
}\r
--- /dev/null
+//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
+//! SPDX-License-Identifier: GPL-3.0-or-later
+
+import { Account, KeyPair } from '../account'
+import { Address } from './address'
+import { ACCOUNT_KEY_BYTE_LENGTH, ACCOUNT_KEY_HEX_LENGTH } from '../constants'
+import { hex } from '../convert'
+import { NanoNaCl } from '../crypto'
+
+export function _load (input: string | Uint8Array<ArrayBuffer> | KeyPair | (string | Uint8Array<ArrayBuffer> | KeyPair)[], type?: 'private'): Account | Account[] | Promise<Account | Account[]> {
+ const isInputArray = Array.isArray(input)
+ const inputs = isInputArray ? input : [input]
+ if (isKeyPairs(inputs) && type === 'private') {
+ return fromPrivate(inputs)
+ .then(r => isInputArray ? r : r[0])
+ } else {
+ return isInputArray ? fromPublic(inputs) : fromPublic(inputs)[0]
+ }
+}
+
+/**
+* Instantiates an Account object from its private key which is used to derive
+* the corresponding public key and then discarded.
+*
+* @param {KeyPair} keypairs - Indexes and keys of the accounts
+* @returns {Promise<Account[]>} Promise for new Account objects
+*/
+async function fromPrivate (keypairs: KeyPair[]): Promise<Account[]> {
+ try {
+ const accounts: Account[] = []
+ for (let keypair of keypairs) {
+ let { index, privateKey } = keypair
+ if (index == null) {
+ throw new RangeError('Index missing for Account')
+ }
+ if (typeof privateKey === 'string' && RegExp(`^[A-F0-9]{${ACCOUNT_KEY_HEX_LENGTH}}$`, 'i').test(privateKey)) {
+ privateKey = hex.toBytes(privateKey)
+ }
+ if (!(privateKey instanceof Uint8Array) || privateKey.every(v => v === 0)) {
+ throw new TypeError('Invalid private key')
+ }
+ if (privateKey.byteLength !== ACCOUNT_KEY_BYTE_LENGTH) {
+ throw new TypeError(`Private key must be ${ACCOUNT_KEY_BYTE_LENGTH} bytes`)
+ }
+ const publicKey = await NanoNaCl.convert(privateKey)
+ const address = new Address(publicKey)
+ this.#isInternal = true
+ const account = new this(address, publicKey, index)
+ this.#isInternal = false
+ accounts.push(account)
+ }
+ return accounts
+ } catch (err) {
+ throw new Error('Failed to import Accounts from private keys', { cause: err })
+ }
+}
+
+/**
+* Instantiates Account objects from public data, each specifying either its
+* public key or its Nano address.
+*
+* @param {(string | Uint8Array<ArrayBuffer>|KeyPair)[]} input - Public keys or addresses of the accounts
+* @returns {Account[]} The instantiated Account objects
+*/
+function fromPublic (input: (string | Uint8Array<ArrayBuffer> | KeyPair)[] | unknown): Account[] {
+ try {
+ const keypairs = isKeyPairs(input)
+ ? input
+ : isKeys(input)
+ ? input.map(i => { return { publicKey: i } as KeyPair })
+ : []
+ if (keypairs.length === 0) {
+ throw new TypeError('Invalid public input for Account')
+ }
+
+ const accounts: Account[] = []
+ for (let keypair of keypairs) {
+ if (keypair.publicKey == null) {
+ throw new TypeError('Account address or public key is required', { cause: keypair.publicKey })
+ }
+ const { index } = keypair
+ const address = new Address(keypair.publicKey)
+ const publicKey = address.toPublicKey()
+ this.#isInternal = true
+ const account = new this(address, publicKey, index)
+ this.#isInternal = false
+ accounts.push(account)
+ }
+ return accounts
+ } catch (err) {
+ console.error(err)
+ throw new TypeError('Failed to import Account from public data', { cause: { err } })
+ }
+}
+
+
+function isKeyPair (input: unknown): input is KeyPair {
+ if (typeof input === 'object') {
+ const obj = input as { [key: string]: unknown }
+ if ('index' in obj && typeof obj.index === 'number') {
+ return true
+ }
+ if ('publicKey' in obj && isKey(obj.publicKey)) {
+ return true
+ }
+ if ('privateKey' in obj && isKey(obj.privateKey)) {
+ return true
+ }
+ }
+ return false
+}
+
+
+function isKeyPairs (input: unknown): input is KeyPair[] {
+ if (Array.isArray(input)) {
+ for (const i of input) {
+ if (!isKeyPair(i)) {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+
+function isKey (input: unknown): input is string | Uint8Array<ArrayBuffer> {
+ return typeof input === 'string' || (input instanceof Uint8Array && 'buffer' in input)
+}
+
+function isKeys (input: unknown): input is (string | Uint8Array<ArrayBuffer>)[] {
+ if (Array.isArray(input)) {
+ for (const i of input) {
+ if (!isKey(i)) {
+ return false
+ }
+ }
+ }
+ return true
+}