From: Chris Duncan Date: Sun, 6 Jul 2025 20:47:23 +0000 (-0700) Subject: Use pool for NanoNaCl conversion and verification. X-Git-Tag: v0.10.5~102 X-Git-Url: https://git.codecow.com/?a=commitdiff_plain;h=3a98f9dacb9a3e5a93d9f184455277af2fc329b4;p=libnemo.git Use pool for NanoNaCl conversion and verification. --- diff --git a/src/lib/account.ts b/src/lib/account.ts index 2fc90d2..7c6452b 100644 --- a/src/lib/account.ts +++ b/src/lib/account.ts @@ -6,7 +6,7 @@ import { ACCOUNT_KEY_BYTE_LENGTH, ACCOUNT_KEY_HEX_LENGTH, ALPHABET, PREFIX, PREF import { base32, bytes, hex, obj, utf8 } from './convert' import { Pool } from './pool' import { Rpc } from './rpc' -import { NanoNaCl, SafeWorker } from '#workers' +import { NanoNaClWorker, SafeWorker } from '#workers' /** * Represents a single Nano address and the associated public key. To include the @@ -16,7 +16,8 @@ import { NanoNaCl, SafeWorker } from '#workers' */ export class Account { static #isInternal: boolean = false - static #poolSafe: Pool + static #poolSafe: Pool = new Pool(SafeWorker) + static #poolNanoNaCl: Pool = new Pool(NanoNaClWorker) #address: string #locked: boolean @@ -71,8 +72,6 @@ export class Account { this.#locked = false this.#pub = publicKey this.#prv = privateKey - Account.#poolSafe ??= new Pool(SafeWorker) - Account.#isInternal = false } /** @@ -101,9 +100,9 @@ export class Account { * @returns {Account} The instantiated Account object */ static fromAddress (address: string, index?: number): Account { - Account.#isInternal = true - Account.validate(address) - const publicKey = Account.#addressToKey(address) + this.#isInternal = true + this.validate(address) + const publicKey = this.#addressToKey(address) const account = new this(address, publicKey, new Uint8Array(32), index) return account } @@ -116,9 +115,9 @@ export class Account { * @returns {Account} The instantiated Account object */ static fromPublicKey (publicKey: string, index?: number): Account { - Account.#isInternal = true - Account.#validateKey(publicKey) - const address = Account.#keyToAddress(publicKey) + this.#isInternal = true + this.#validateKey(publicKey) + const address = this.#keyToAddress(publicKey) const account = new this(address, publicKey, new Uint8Array(32), index) return account } @@ -131,12 +130,21 @@ export class Account { * @param {number} [index] - Account number used when deriving the key * @returns {Account} A new Account object */ - static fromPrivateKey (privateKey: string | Uint8Array, index?: number): Account { + static async fromPrivateKey (privateKey: string | Uint8Array, index?: number): Promise { if (typeof privateKey === 'string') privateKey = hex.toBytes(privateKey) - Account.#isInternal = true - Account.#validateKey(privateKey) - const publicKey = NanoNaCl.convert(privateKey) - const address = Account.#keyToAddress(publicKey) + this.#isInternal = true + this.#validateKey(privateKey) + let publicKey: string + try { + const result = (await this.#poolNanoNaCl.assign({ + method: 'convert', + privateKey: bytes.toArray(privateKey) + }))[0] + publicKey = result.publicKey + } catch (err) { + throw new Error(`Failed to derive public key from private key`, { cause: err }) + } + const address = this.#keyToAddress(publicKey) const account = new this(address, publicKey, privateKey, index) return account } diff --git a/src/lib/block.ts b/src/lib/block.ts index 3686e3c..63e4c4b 100644 --- a/src/lib/block.ts +++ b/src/lib/block.ts @@ -8,7 +8,7 @@ import { BURN_ADDRESS, PREAMBLE, DIFFICULTY_RECEIVE, DIFFICULTY_SEND } from './c import { dec, hex } from './convert' import { Pool } from './pool' import { Rpc } from './rpc' -import { NanoNaCl, NanoNaClWorker } from '#workers' +import { NanoNaClWorker } from '#workers' /** * Represents a block as defined by the Nano cryptocurrency protocol. The Block @@ -16,7 +16,8 @@ import { NanoNaCl, NanoNaClWorker } from '#workers' * of three derived classes: SendBlock, ReceiveBlock, ChangeBlock. */ abstract class Block { - static #poolNanoNaCl: Pool + static #poolNanoNaCl: Pool = new Pool(NanoNaClWorker) + account: Account type: string = 'state' abstract subtype: 'send' | 'receive' | 'change' @@ -38,7 +39,6 @@ abstract class Block { } else { throw new TypeError('Invalid account') } - Block.#poolNanoNaCl ??= new Pool(NanoNaClWorker) } /** @@ -200,11 +200,17 @@ abstract class Block { if (!key) { throw new Error('Provide a key for block signature verification.') } - return NanoNaCl.verify( - hex.toBytes(this.hash), - hex.toBytes(this.signature ?? ''), - hex.toBytes(key) - ) + try { + const result = (await Block.#poolNanoNaCl.assign({ + method: 'verify', + msg: hex.toArray(this.hash), + signature: hex.toArray(this.signature ?? ''), + publicKey: hex.toArray(key) + }))[0] + return result.isVerified + } catch (err) { + throw new Error(`Failed to derive public key from private key`, { cause: err }) + } } } diff --git a/src/lib/convert.ts b/src/lib/convert.ts index f2a0590..a1d5733 100644 --- a/src/lib/convert.ts +++ b/src/lib/convert.ts @@ -76,6 +76,19 @@ export class bin { } export class bytes { + /** + * Convert a Uint8Array to an array of decimal byte values. + * + * @param {Uint8Array} bytes - Byte array to convert + * @param {number}[padding=0] - Minimum length of the resulting array padded as necessary with starting 0 values + * @returns {number[]} Decimal array representation of the input value + */ + static toArray (bytes: Uint8Array, padding: number = 0): number[] { + if (!(bytes instanceof Uint8Array)) { + throw new TypeError('bytes must be Uint8Array') + } + return [...bytes.values()] + } /** * Converts a Uint8Aarray of bytes to a base32 string. * diff --git a/src/lib/wallets/wallet.ts b/src/lib/wallets/wallet.ts index b2efda7..b3e87f1 100644 --- a/src/lib/wallets/wallet.ts +++ b/src/lib/wallets/wallet.ts @@ -26,8 +26,7 @@ export type KeyPair = { export abstract class Wallet { abstract ckd (index: number[]): Promise - static #poolNanoNacl: Pool - static #poolSafe: Pool + static #poolSafe: Pool = new Pool(SafeWorker) #accounts: AccountList #id: Entropy @@ -49,8 +48,6 @@ export abstract class Wallet { this.#id = id this.#m = mnemonic ?? null this.#s = seed ?? null - Wallet.#poolNanoNacl ??= new Pool(NanoNaClWorker) - Wallet.#poolSafe ??= new Pool(SafeWorker) } /** @@ -112,21 +109,13 @@ export abstract class Wallet { } } if (indexes.length > 0) { - let results = await this.ckd(indexes) - const data: any = [] - results.forEach(r => data.push({ - method: 'convert', - privateKey: r.privateKey, - index: r.index - })) - const keypairs: KeyPair[] = await Wallet.#poolNanoNacl.assign(data) + const keypairs = await this.ckd(indexes) for (const keypair of keypairs) { - if (keypair.privateKey == null) throw new RangeError('Account private key missing') - if (keypair.publicKey == null) throw new RangeError('Account public key missing') - if (keypair.index == null) throw new RangeError('Account keys derived but index missing') const { privateKey, index } = keypair - output[keypair.index] = Account.fromPrivateKey(privateKey, index) - this.#accounts[keypair.index] = output[keypair.index] + if (privateKey == null) throw new RangeError('Account private key missing') + if (index == null) throw new RangeError('Account keys derived but index missing') + output[index] = await Account.fromPrivateKey(privateKey, index) + this.#accounts[index] = output[index] } } return output diff --git a/src/lib/workers/nano-nacl.ts b/src/lib/workers/nano-nacl.ts index 834d374..0b2dabf 100644 --- a/src/lib/workers/nano-nacl.ts +++ b/src/lib/workers/nano-nacl.ts @@ -28,12 +28,16 @@ export class NanoNaCl extends WorkerInterface { for (let d of data) { try { switch (d.method) { + case 'convert': { + d.publicKey = await this.convert(Uint8Array.from(d.privateKey)) + break + } case 'detached': { d.signature = await this.detached(Uint8Array.from(d.msg), Uint8Array.from(d.privateKey)) break } - case 'convert': { - d.publicKey = await this.convert(d.privateKey) + case 'verify': { + d.isVerified = await this.verify(Uint8Array.from(d.msg), Uint8Array.from(d.signature), Uint8Array.from(d.publicKey)) break } default: { diff --git a/test/GLOBALS.mjs b/test/GLOBALS.mjs index 14ab050..7356b28 100644 --- a/test/GLOBALS.mjs +++ b/test/GLOBALS.mjs @@ -141,7 +141,7 @@ export function test (name, opts, fn) { try { return fn .then(() => pass(name)) - .catch((err) => { fail(`${name}: ${err}`) }) + .catch((err) => fail(`${name}: ${err}`)) } catch (err) { fail(`${name}: ${err.message}`) fail(err) @@ -150,7 +150,7 @@ export function test (name, opts, fn) { try { return fn() .then(() => pass(name)) - .catch((err) => fail(`${name}: ${err.message}`)) + .catch((err) => fail(`${name}: ${err}`)) } catch (err) { fail(`${name}: ${err.message}`) fail(err) @@ -160,7 +160,7 @@ export function test (name, opts, fn) { fn() pass(name) } catch (err) { - fail(`${name}: ${err.message}`) + fail(`${name}: ${err}`) fail(err) } } else {