import { base32, bytes, hex, obj, utf8 } from './convert'\r
import { Pool } from './pool'\r
import { Rpc } from './rpc'\r
-import { NanoNaCl, SafeWorker } from '#workers'\r
+import { NanoNaClWorker, SafeWorker } from '#workers'\r
\r
/**\r
* Represents a single Nano address and the associated public key. To include the\r
*/\r
export class Account {\r
static #isInternal: boolean = false\r
- static #poolSafe: Pool\r
+ static #poolSafe: Pool = new Pool(SafeWorker)\r
+ static #poolNanoNaCl: Pool = new Pool(NanoNaClWorker)\r
\r
#address: string\r
#locked: boolean\r
this.#locked = false\r
this.#pub = publicKey\r
this.#prv = privateKey\r
- Account.#poolSafe ??= new Pool(SafeWorker)\r
- Account.#isInternal = false\r
}\r
\r
/**\r
* @returns {Account} The instantiated Account object\r
*/\r
static fromAddress (address: string, index?: number): Account {\r
- Account.#isInternal = true\r
- Account.validate(address)\r
- const publicKey = Account.#addressToKey(address)\r
+ this.#isInternal = true\r
+ this.validate(address)\r
+ const publicKey = this.#addressToKey(address)\r
const account = new this(address, publicKey, new Uint8Array(32), index)\r
return account\r
}\r
* @returns {Account} The instantiated Account object\r
*/\r
static fromPublicKey (publicKey: string, index?: number): Account {\r
- Account.#isInternal = true\r
- Account.#validateKey(publicKey)\r
- const address = Account.#keyToAddress(publicKey)\r
+ this.#isInternal = true\r
+ this.#validateKey(publicKey)\r
+ const address = this.#keyToAddress(publicKey)\r
const account = new this(address, publicKey, new Uint8Array(32), index)\r
return account\r
}\r
* @param {number} [index] - Account number used when deriving the key\r
* @returns {Account} A new Account object\r
*/\r
- static fromPrivateKey (privateKey: string | Uint8Array<ArrayBuffer>, index?: number): Account {\r
+ static async fromPrivateKey (privateKey: string | Uint8Array<ArrayBuffer>, index?: number): Promise<Account> {\r
if (typeof privateKey === 'string') privateKey = hex.toBytes(privateKey)\r
- Account.#isInternal = true\r
- Account.#validateKey(privateKey)\r
- const publicKey = NanoNaCl.convert(privateKey)\r
- const address = Account.#keyToAddress(publicKey)\r
+ this.#isInternal = true\r
+ this.#validateKey(privateKey)\r
+ let publicKey: string\r
+ try {\r
+ const result = (await this.#poolNanoNaCl.assign({\r
+ method: 'convert',\r
+ privateKey: bytes.toArray(privateKey)\r
+ }))[0]\r
+ publicKey = result.publicKey\r
+ } catch (err) {\r
+ throw new Error(`Failed to derive public key from private key`, { cause: err })\r
+ }\r
+ const address = this.#keyToAddress(publicKey)\r
const account = new this(address, publicKey, privateKey, index)\r
return account\r
}\r
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
* 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'
} else {
throw new TypeError('Invalid account')
}
- Block.#poolNanoNaCl ??= new Pool(NanoNaClWorker)
}
/**
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 })
+ }
}
}
}\r
\r
export class bytes {\r
+ /**\r
+ * Convert a Uint8Array to an array of decimal byte values.\r
+ *\r
+ * @param {Uint8Array} bytes - Byte array to convert\r
+ * @param {number}[padding=0] - Minimum length of the resulting array padded as necessary with starting 0 values\r
+ * @returns {number[]} Decimal array representation of the input value\r
+ */\r
+ static toArray (bytes: Uint8Array, padding: number = 0): number[] {\r
+ if (!(bytes instanceof Uint8Array)) {\r
+ throw new TypeError('bytes must be Uint8Array')\r
+ }\r
+ return [...bytes.values()]\r
+ }\r
/**\r
* Converts a Uint8Aarray of bytes to a base32 string.\r
*\r
export abstract class Wallet {\r
abstract ckd (index: number[]): Promise<KeyPair[]>\r
\r
- static #poolNanoNacl: Pool\r
- static #poolSafe: Pool\r
+ static #poolSafe: Pool = new Pool(SafeWorker)\r
\r
#accounts: AccountList\r
#id: Entropy\r
this.#id = id\r
this.#m = mnemonic ?? null\r
this.#s = seed ?? null\r
- Wallet.#poolNanoNacl ??= new Pool(NanoNaClWorker)\r
- Wallet.#poolSafe ??= new Pool(SafeWorker)\r
}\r
\r
/**\r
}\r
}\r
if (indexes.length > 0) {\r
- let results = await this.ckd(indexes)\r
- const data: any = []\r
- results.forEach(r => data.push({\r
- method: 'convert',\r
- privateKey: r.privateKey,\r
- index: r.index\r
- }))\r
- const keypairs: KeyPair[] = await Wallet.#poolNanoNacl.assign(data)\r
+ const keypairs = await this.ckd(indexes)\r
for (const keypair of keypairs) {\r
- if (keypair.privateKey == null) throw new RangeError('Account private key missing')\r
- if (keypair.publicKey == null) throw new RangeError('Account public key missing')\r
- if (keypair.index == null) throw new RangeError('Account keys derived but index missing')\r
const { privateKey, index } = keypair\r
- output[keypair.index] = Account.fromPrivateKey(privateKey, index)\r
- this.#accounts[keypair.index] = output[keypair.index]\r
+ if (privateKey == null) throw new RangeError('Account private key missing')\r
+ if (index == null) throw new RangeError('Account keys derived but index missing')\r
+ output[index] = await Account.fromPrivateKey(privateKey, index)\r
+ this.#accounts[index] = output[index]\r
}\r
}\r
return output\r
for (let d of data) {\r
try {\r
switch (d.method) {\r
+ case 'convert': {\r
+ d.publicKey = await this.convert(Uint8Array.from(d.privateKey))\r
+ break\r
+ }\r
case 'detached': {\r
d.signature = await this.detached(Uint8Array.from(d.msg), Uint8Array.from(d.privateKey))\r
break\r
}\r
- case 'convert': {\r
- d.publicKey = await this.convert(d.privateKey)\r
+ case 'verify': {\r
+ d.isVerified = await this.verify(Uint8Array.from(d.msg), Uint8Array.from(d.signature), Uint8Array.from(d.publicKey))\r
break\r
}\r
default: {\r
try {
return fn
.then(() => pass(name))
- .catch((err) => { fail(`${name}: ${err}`) })
+ .catch((err) => fail(`${name}: ${err}`))
} catch (err) {
fail(`${name}: ${err.message}`)
fail(err)
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)
fn()
pass(name)
} catch (err) {
- fail(`${name}: ${err.message}`)
+ fail(`${name}: ${err}`)
fail(err)
}
} else {