From 47b06546b05aba7b9c885ab6e7a98365a15e6426 Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Sun, 10 Aug 2025 13:22:08 -0700 Subject: [PATCH] Move data validation for wallet load into separate load module. Refactor how Ledger calls super. Revert wallet create and load parameter swap. --- README.md | 12 ++++----- src/lib/wallet/index.ts | 50 +++++++++++++++-------------------- src/lib/wallet/ledger.ts | 9 +++---- src/lib/wallet/load.ts | 19 ++++++++++--- test/perf.account.mjs | 8 +++--- test/perf.wallet.mjs | 4 +-- test/test.blocks.mjs | 6 ++--- test/test.create-wallet.mjs | 8 +++--- test/test.derive-accounts.mjs | 12 ++++----- test/test.import-wallet.mjs | 44 +++++++++++++++--------------- test/test.lock-unlock.mjs | 12 ++++----- test/test.tools.mjs | 6 ++--- 12 files changed, 98 insertions(+), 92 deletions(-) diff --git a/README.md b/README.md index 56a38ee..9b8572a 100644 --- a/README.md +++ b/README.md @@ -68,13 +68,13 @@ usage. ```javascript import { Wallet } from 'libnemo' -const wallet = await Wallet.create(password) -const wallet = await Wallet.load(password, mnemonic, salt?) -const wallet = await Wallet.load(password, seed) +const wallet = await Wallet.create('BIP-44', password) +const wallet = await Wallet.load('BIP-44', password, mnemonic, salt?) +const wallet = await Wallet.load('BIP-44', password, seed) -const wallet = await Wallet.create(password) -const wallet = await Wallet.load(password, seed) -const wallet = await Wallet.load(password, mnemonic) +const wallet = await Wallet.create('BLAKE2b', password) +const wallet = await Wallet.load('BLAKE2b', password, seed) +const wallet = await Wallet.load('BLAKE2b', password, mnemonic) ``` ```javascript diff --git a/src/lib/wallet/index.ts b/src/lib/wallet/index.ts index 2c2f29a..8ff2325 100644 --- a/src/lib/wallet/index.ts +++ b/src/lib/wallet/index.ts @@ -42,16 +42,20 @@ export class Wallet { * @param {string} [salt=''] - Used when generating the final seed * @returns {Wallet} A newly instantiated Wallet */ - static async create (password: string, type: 'BIP-44' | 'BLAKE2b', mnemonicSalt?: string): Promise - static async create (password: unknown, type: unknown, mnemonicSalt?: unknown): Promise { - if (typeof password !== 'string') { - throw new TypeError('Password must be a string') + static async create (type: WalletType, password: string, mnemonicSalt?: string): Promise + static async create (type: unknown, password: unknown, mnemonicSalt?: unknown): Promise { + if (type === 'Ledger') { + Wallet.#isInternal = true + return new this(type) } - const passwordBuffer = utf8.toBuffer(password) - password = undefined if (type !== 'BIP-44' && type !== 'BLAKE2b') { throw new TypeError('Invalid wallet type') } + if (typeof password !== 'string') { + throw new TypeError('Invalid password', { cause: typeof password }) + } + const passwordBuffer = utf8.toBuffer(password) + password = undefined if (mnemonicSalt !== undefined && typeof mnemonicSalt !== 'string') { throw new TypeError('Mnemonic salt must be a string') } @@ -83,41 +87,27 @@ export class Wallet { * Imports an existing HD wallet by using an entropy value generated using a * cryptographically strong pseudorandom number generator.NamedD * - * @param {string} password - Encrypts the wallet to lock and unlock it. Discard as soon as possible after loading the wallet. * @param {string} type - Algorithm used to generate wallet and child accounts + * @param {string} password - Encrypts the wallet to lock and unlock it. Discard as soon as possible after loading the wallet. * @param {string} seed - Used to derive child accounts * @returns Wallet in a locked state */ - static async load (password: string, type: 'BIP-44' | 'BLAKE2b', seed: string): Promise + static async load (type: 'BIP-44' | 'BLAKE2b', password: string, seed: string): Promise /** * Imports an existing HD wallet by using an entropy value generated using a * cryptographically strong pseudorandom number generator. * - * @param {string} password - Encrypts the wallet to lock and unlock it. Discard as soon as possible after loading the wallet. * @param {string} type - Algorithm used to generate wallet and child accounts + * @param {string} password - Encrypts the wallet to lock and unlock it. Discard as soon as possible after loading the wallet. * @param {string} mnemonicPhrase - Used to derive the wallet seed * @param {string} [mnemonicSalt] - Used to alter the seed derived from the mnemonic phrase * @returns Wallet in a locked state */ - static async load (password: string, type: 'BIP-44' | 'BLAKE2b', mnemonicPhrase: string, mnemonicSalt?: string): Promise - static async load (password: unknown, type: unknown, secret: unknown, mnemonicSalt?: unknown): Promise { - if (typeof password !== 'string') { - throw new TypeError('Password must be a string') - } - const passwordBuffer = utf8.toBuffer(password) - password = undefined - if (type !== 'BIP-44' && type !== 'BLAKE2b') { - throw new TypeError('Invalid wallet type', { cause: type }) - } - if (typeof secret !== 'string') { - throw new TypeError('Wallet secret must be a string') - } - if (mnemonicSalt !== undefined && typeof mnemonicSalt !== 'string') { - throw new TypeError('Mnemonic salt must be a string') - } + static async load (type: 'BIP-44' | 'BLAKE2b', password: string, mnemonicPhrase: string, mnemonicSalt?: string): Promise + static async load (type: WalletType, password: string, secret: string, mnemonicSalt?: string): Promise { Wallet.#isInternal = true const self = new this(type) - await _load(self, passwordBuffer, secret, mnemonicSalt) + await _load(self, password, secret, mnemonicSalt) return self } @@ -185,10 +175,14 @@ export class Wallet { } } - constructor (type: WalletType, id?: string) { - if (!Wallet.#isInternal && type !== 'Ledger') { + constructor (type: WalletType, id?: string) + constructor (type: unknown, id?: string) { + if (!Wallet.#isInternal) { throw new Error(`Wallet cannot be instantiated directly. Use 'await Wallet.create()' instead.`) } + if (type !== 'BIP-44' && type !== 'BLAKE2b' && type !== 'Ledger') { + throw new TypeError('Invalid wallet type', { cause: type }) + } Wallet.#isInternal = false this.#accounts = new AccountList() this.#id = id ?? crypto.randomUUID() diff --git a/src/lib/wallet/ledger.ts b/src/lib/wallet/ledger.ts index 98e93d2..773a028 100644 --- a/src/lib/wallet/ledger.ts +++ b/src/lib/wallet/ledger.ts @@ -21,12 +21,13 @@ import { DeviceStatus, LedgerAccountResponse, LedgerResponse, LedgerSignResponse * is responsible for using Ledger technology to back up these pieces of data. */ export class Ledger extends Wallet { - static #isInternal: boolean = false static #derivationPath: Uint8Array = new Uint8Array([ LEDGER_ADPU_CODES.bip32DerivationLevel, ...dec.toBytes(BIP44_PURPOSE + HARDENED_OFFSET, 4), ...dec.toBytes(BIP44_COIN_NANO + HARDENED_OFFSET, 4) ]) + static #isInternal: boolean = false + static INTERNAL: Symbol = Symbol('Ledger') static DynamicTransport: typeof TransportBLE | typeof TransportUSB | typeof TransportHID static get listenTimeout (): 30000 { return 30000 } @@ -62,7 +63,7 @@ export class Ledger extends Wallet { static async create (): Promise { try { if (this.isUnsupported) throw new Error('Browser is unsupported') - Ledger.#isInternal = true + this.#isInternal = true const self = new this() await Database.add({ [self.id]: { id: self.id, type: 'Ledger' } }, Wallet.DB_NAME) return self @@ -80,7 +81,6 @@ export class Ledger extends Wallet { } #accounts: AccountList - #vault: undefined #status: DeviceStatus = 'DISCONNECTED' get status (): DeviceStatus { return this.#status } @@ -91,7 +91,6 @@ export class Ledger extends Wallet { Ledger.#isInternal = false super('Ledger') this.#accounts = new AccountList() - this.#vault = undefined } /** @@ -354,7 +353,7 @@ export class Ledger extends Wallet { */ async verify (mnemonic: string): Promise async verify (secret: string): Promise { - const testWallet = await Wallet.load('', 'BIP-44', secret) + const testWallet = await Wallet.load('BIP-44', '', secret) await testWallet.unlock('') const testAccount = await testWallet.account(0) const testOpenBlock = await new Block(testAccount.address, '0', testAccount.publicKey, testAccount.address) diff --git a/src/lib/wallet/load.ts b/src/lib/wallet/load.ts index 2a2a074..b4d7bad 100644 --- a/src/lib/wallet/load.ts +++ b/src/lib/wallet/load.ts @@ -3,7 +3,7 @@ import { Bip39 } from '#crypto' import { NamedData } from '#types' -import { hex } from '../convert' +import { hex, utf8 } from '../convert' import { Database } from '../database' import { Wallet } from '.' @@ -15,13 +15,24 @@ import { Wallet } from '.' * @param {string} [salt=''] - Used when generating the final seed * @returns {Wallet} A newly instantiated Wallet */ -export async function _load (wallet: Wallet, password: ArrayBuffer, secret: string, mnemonicSalt?: string): Promise { +export async function _load (wallet: Wallet, password: string, secret: string, mnemonicSalt?: string): Promise +export async function _load (wallet: Wallet, password: unknown, secret: unknown, mnemonicSalt?: unknown): Promise { + if (typeof password !== 'string') { + throw new TypeError('Password must be a string') + } + if (typeof secret !== 'string') { + throw new TypeError('Wallet secret must be a string') + } + if (mnemonicSalt !== undefined && typeof mnemonicSalt !== 'string') { + throw new TypeError('Mnemonic salt must be a string') + } try { const data: NamedData = { action: 'load', type: wallet.type, - password + password: utf8.toBuffer(password) } + password = undefined if (/^(?:[A-F0-9]{64}){1,2}$/i.test(secret)) { data.seed = hex.toBuffer(secret) } else if (await Bip39.validate(secret)) { @@ -30,6 +41,8 @@ export async function _load (wallet: Wallet, password: ArrayBuffer, secret: stri } else { throw new TypeError('Invalid wallet data') } + secret = undefined + mnemonicSalt = undefined const result = wallet.vault.request(data) const { iv, salt, encrypted } = await result const record = { diff --git a/test/perf.account.mjs b/test/perf.account.mjs index 0e5919a..90d8384 100644 --- a/test/perf.account.mjs +++ b/test/perf.account.mjs @@ -21,7 +21,7 @@ await Promise.all([ const COUNT = 0x200 await test(`Time to create ${COUNT} BIP-44 accounts`, async () => { - const wallet = await Wallet.create(NANO_TEST_VECTORS.PASSWORD, 'BIP-44') + const wallet = await Wallet.create('BIP-44', NANO_TEST_VECTORS.PASSWORD) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const start = performance.now() @@ -35,7 +35,7 @@ await Promise.all([ }) await test(`Time to create ${COUNT} BLAKE2b accounts`, async () => { - const wallet = await Wallet.create(NANO_TEST_VECTORS.PASSWORD, 'BLAKE2b') + const wallet = await Wallet.create('BLAKE2b', NANO_TEST_VECTORS.PASSWORD) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const start = performance.now() @@ -49,7 +49,7 @@ await Promise.all([ }) await test(`Time to create 1 BIP-44 account ${COUNT} times`, async () => { - const wallet = await Wallet.create(NANO_TEST_VECTORS.PASSWORD, 'BIP-44') + const wallet = await Wallet.create('BIP-44', NANO_TEST_VECTORS.PASSWORD) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const times = [] @@ -65,7 +65,7 @@ await Promise.all([ }) await test(`Time to create 1 BLAKE2b account ${COUNT} times`, async () => { - const wallet = await Wallet.create(NANO_TEST_VECTORS.PASSWORD, 'BLAKE2b') + const wallet = await Wallet.create('BLAKE2b', NANO_TEST_VECTORS.PASSWORD) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const times = [] diff --git a/test/perf.wallet.mjs b/test/perf.wallet.mjs index 30e1a0a..6764e9e 100644 --- a/test/perf.wallet.mjs +++ b/test/perf.wallet.mjs @@ -24,7 +24,7 @@ await Promise.all([ const times = [] for (let i = 0; i < COUNT; i++) { const start = performance.now() - const wallet = await Wallet.create(NANO_TEST_VECTORS.PASSWORD, 'BIP-44') + const wallet = await Wallet.create('BIP-44', NANO_TEST_VECTORS.PASSWORD) const end = performance.now() times.push(end - start) await wallet.destroy() @@ -36,7 +36,7 @@ await Promise.all([ const times = [] for (let i = 0; i < COUNT; i++) { const start = performance.now() - const wallet = await Wallet.create(NANO_TEST_VECTORS.PASSWORD, 'BLAKE2b') + const wallet = await Wallet.create('BLAKE2b', NANO_TEST_VECTORS.PASSWORD) const end = performance.now() times.push(end - start) await wallet.destroy() diff --git a/test/test.blocks.mjs b/test/test.blocks.mjs index 900b55e..81fe264 100644 --- a/test/test.blocks.mjs +++ b/test/test.blocks.mjs @@ -98,7 +98,7 @@ await Promise.all([ const { ADDRESS_0, BIP39_SEED, BLAKE2B_ADDRESS_1, BLAKE2B_PUBLIC_1, BLAKE2B_SEED, OPEN_BLOCK, PASSWORD, PRIVATE_0, RECEIVE_BLOCK, SEND_BLOCK } = NANO_TEST_VECTORS await test('sign open block with BLAKE2b wallet', async () => { - const wallet = await Wallet.load(PASSWORD, 'BLAKE2b', BLAKE2B_SEED) + const wallet = await Wallet.load('BLAKE2b', PASSWORD, BLAKE2B_SEED) await assert.resolves(wallet.unlock(PASSWORD)) const block = new Block(BLAKE2B_ADDRESS_1, '0', OPEN_BLOCK.previous, OPEN_BLOCK.representative) @@ -111,7 +111,7 @@ await Promise.all([ }) await test('sign open block with BIP-44 wallet', async () => { - const wallet = await Wallet.load(PASSWORD, 'BIP-44', BIP39_SEED) + const wallet = await Wallet.load('BIP-44', PASSWORD, BIP39_SEED) await assert.resolves(wallet.unlock(PASSWORD)) const block = new Block(ADDRESS_0, '0', OPEN_BLOCK.previous, OPEN_BLOCK.representative) @@ -123,7 +123,7 @@ await Promise.all([ }) await test('fail to sign open block with wallet when locked', async () => { - const wallet = await Wallet.load(PASSWORD, 'BIP-44', BIP39_SEED) + const wallet = await Wallet.load('BIP-44', PASSWORD, BIP39_SEED) const block = new Block(ADDRESS_0, '0', OPEN_BLOCK.previous, OPEN_BLOCK.representative) .receive(OPEN_BLOCK.link, OPEN_BLOCK.balance) diff --git a/test/test.create-wallet.mjs b/test/test.create-wallet.mjs index 980c56b..4b009a2 100644 --- a/test/test.create-wallet.mjs +++ b/test/test.create-wallet.mjs @@ -21,14 +21,14 @@ await Promise.all([ const { PASSWORD } = NANO_TEST_VECTORS await test('destroy BIP-44 wallet before unlocking', async () => { - const wallet = await Wallet.create(PASSWORD, 'BIP-44') + const wallet = await Wallet.create('BIP-44', PASSWORD) await assert.resolves(wallet.destroy()) await assert.rejects(wallet.unlock(PASSWORD)) }) await test('BIP-44 wallet with random entropy', async () => { - const wallet = await Wallet.create(PASSWORD, 'BIP-44') + const wallet = await Wallet.create('BIP-44', PASSWORD) await wallet.unlock(PASSWORD) assert.ok('id' in wallet) @@ -49,7 +49,7 @@ await Promise.all([ }) await test('BLAKE2b wallet with random entropy', async () => { - const wallet = await Wallet.create(PASSWORD, 'BLAKE2b') + const wallet = await Wallet.create('BLAKE2b', PASSWORD) await wallet.unlock(PASSWORD) assert.ok('id' in wallet) @@ -73,7 +73,7 @@ await Promise.all([ const invalidArgs = [null, true, false, 0, 1, 2, { foo: 'bar' }] for (const arg of invalidArgs) { //@ts-expect-error - await assert.rejects(Wallet.create(PASSWORD, 'BIP-44', arg)) + await assert.rejects(Wallet.create('BIP-44', PASSWORD, arg)) } }) diff --git a/test/test.derive-accounts.mjs b/test/test.derive-accounts.mjs index dda65fb..1fee8fd 100644 --- a/test/test.derive-accounts.mjs +++ b/test/test.derive-accounts.mjs @@ -20,7 +20,7 @@ await Promise.all([ suite('Derive accounts from BIP-44 wallet', async () => { await test('derive the first account from the given BIP-44 seed', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', NANO_TEST_VECTORS.BIP39_SEED) + const wallet = await Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const account = await wallet.account() @@ -36,7 +36,7 @@ await Promise.all([ }) await test('derive low indexed accounts from the given BIP-44 seed', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', NANO_TEST_VECTORS.BIP39_SEED) + const wallet = await Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const accounts = await wallet.accounts(1, 2) @@ -52,7 +52,7 @@ await Promise.all([ }) await test('derive high indexed accounts from the given seed', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', NANO_TEST_VECTORS.BIP39_SEED) + const wallet = await Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const accounts = await wallet.accounts(0x70000000, 0x7000000f) @@ -72,7 +72,7 @@ await Promise.all([ suite('Derive accounts from BLAKE2b wallet', async () => { await test('derive the second account from the given BLAKE2b seed', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BLAKE2b', NANO_TEST_VECTORS.BLAKE2B_SEED) + const wallet = await Wallet.load('BLAKE2b', NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BLAKE2B_SEED) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const account = await wallet.account(1) @@ -90,7 +90,7 @@ await Promise.all([ }) await test('derive low indexed accounts from the given BLAKE2B seed', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BLAKE2b', NANO_TEST_VECTORS.BLAKE2B_SEED) + const wallet = await Wallet.load('BLAKE2b', NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BLAKE2B_SEED) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const accounts = await wallet.accounts(2, 3) @@ -106,7 +106,7 @@ await Promise.all([ }) await test('derive high indexed accounts from the given seed', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BLAKE2b', NANO_TEST_VECTORS.BLAKE2B_SEED) + const wallet = await Wallet.load('BLAKE2b', NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BLAKE2B_SEED) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const accounts = await wallet.accounts(0x70000000, 0x7000000f) diff --git a/test/test.import-wallet.mjs b/test/test.import-wallet.mjs index 925f797..784199c 100644 --- a/test/test.import-wallet.mjs +++ b/test/test.import-wallet.mjs @@ -24,7 +24,7 @@ await Promise.all([ suite('Import wallets', async () => { await test('nano.org BIP-44 test vector mnemonic', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD) + const wallet = await Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const account = await wallet.account() @@ -38,7 +38,7 @@ await Promise.all([ }) await test('nano.org BIP-44 test vector seed with no mnemonic', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', NANO_TEST_VECTORS.BIP39_SEED) + const wallet = await Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const account = await wallet.account() @@ -54,7 +54,7 @@ await Promise.all([ }) await test('Trezor-derived BIP-44 entropy for 12-word mnemonic', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', CUSTOM_TEST_VECTORS.MNEMONIC_0) + const wallet = await Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, CUSTOM_TEST_VECTORS.MNEMONIC_0) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const account = await wallet.account() @@ -67,7 +67,7 @@ await Promise.all([ }) await test('Trezor-derived BIP-44 entropy for 15-word mnemonic', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', CUSTOM_TEST_VECTORS.MNEMONIC_1) + const wallet = await Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, CUSTOM_TEST_VECTORS.MNEMONIC_1) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const account = await wallet.account() @@ -80,7 +80,7 @@ await Promise.all([ }) await test('Trezor-derived BIP-44 entropy for 18-word mnemonic', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', CUSTOM_TEST_VECTORS.MNEMONIC_2) + const wallet = await Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, CUSTOM_TEST_VECTORS.MNEMONIC_2) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const account = await wallet.account() @@ -93,7 +93,7 @@ await Promise.all([ }) await test('Trezor-derived BIP-44 entropy for 21-word mnemonic', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', CUSTOM_TEST_VECTORS.MNEMONIC_3) + const wallet = await Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, CUSTOM_TEST_VECTORS.MNEMONIC_3) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const account = await wallet.account() @@ -106,7 +106,7 @@ await Promise.all([ }) await test('BIP-44 zero-string entropy', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', TREZOR_TEST_VECTORS.MNEMONIC_0, TREZOR_TEST_VECTORS.PASSWORD) + const wallet = await Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.MNEMONIC_0, TREZOR_TEST_VECTORS.PASSWORD) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const accounts = await wallet.accounts(0, 3) @@ -126,7 +126,7 @@ await Promise.all([ }) await test('BLAKE2b zero-string seed', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BLAKE2b', TREZOR_TEST_VECTORS.MNEMONIC_0) + const wallet = await Wallet.load('BLAKE2b', NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.MNEMONIC_0) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const accounts = await wallet.accounts(0, 3) @@ -148,7 +148,7 @@ await Promise.all([ }) await test('Trezor-derived BLAKE2b test vectors verified with third-party libraries', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BLAKE2b', TREZOR_TEST_VECTORS.MNEMONIC_1) + const wallet = await Wallet.load('BLAKE2b', NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.MNEMONIC_1) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const accounts = await wallet.accounts(0, 1) @@ -167,14 +167,14 @@ await Promise.all([ }) await test('BLAKE2b seed creates identical wallet as its derived mnemonic', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BLAKE2b', TREZOR_TEST_VECTORS.ENTROPY_2) + const wallet = await Wallet.load('BLAKE2b', NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.ENTROPY_2) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const walletAccount = await wallet.account() assert.ok(await wallet.verify(TREZOR_TEST_VECTORS.MNEMONIC_2)) assert.ok(await wallet.verify(TREZOR_TEST_VECTORS.ENTROPY_2)) - const imported = await Wallet.load(TREZOR_TEST_VECTORS.PASSWORD, 'BLAKE2b', TREZOR_TEST_VECTORS.MNEMONIC_2) + const imported = await Wallet.load('BLAKE2b', TREZOR_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.MNEMONIC_2) await imported.unlock(TREZOR_TEST_VECTORS.PASSWORD) const importedAccount = await imported.account() @@ -187,7 +187,7 @@ await Promise.all([ }) await test('BLAKE2b mnemonic for maximum seed value', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BLAKE2b', TREZOR_TEST_VECTORS.MNEMONIC_3) + const wallet = await Wallet.load('BLAKE2b', NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.MNEMONIC_3) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const account = await wallet.account() @@ -201,27 +201,27 @@ await Promise.all([ }) await test('Reject invalid seed', async () => { - await assert.rejects(Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', '6CAF5A42BB8074314AAE20295975ECE663BE7AAD945A73613D193B0CC41C797')) - await assert.rejects(Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', '6CAF5A42BB8074314AAE20295975ECE663BE7AAD945A73613D193B0CC41C79701')) - await assert.rejects(Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', TREZOR_TEST_VECTORS.ENTROPY_0.replaceAll(/./g, 'x'))) + await assert.rejects(Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, '6CAF5A42BB8074314AAE20295975ECE663BE7AAD945A73613D193B0CC41C797')) + await assert.rejects(Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, '6CAF5A42BB8074314AAE20295975ECE663BE7AAD945A73613D193B0CC41C79701')) + await assert.rejects(Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.ENTROPY_0.replaceAll(/./g, 'x'))) }) await test('Reject invalid length seed', async () => { - await assert.rejects(Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', NANO_TEST_VECTORS.BIP39_SEED + 'f'), + await assert.rejects(Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED + 'f'), `Expected a ${NANO_TEST_VECTORS.BIP39_SEED.length}-character seed, but received ${NANO_TEST_VECTORS.BIP39_SEED.length + 1}-character string.`) - await assert.rejects(Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', NANO_TEST_VECTORS.BIP39_SEED.slice(0, -1)), + await assert.rejects(Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED.slice(0, -1)), `Expected a ${NANO_TEST_VECTORS.BIP39_SEED.length}-character seed, but received ${NANO_TEST_VECTORS.BIP39_SEED.length - 1}-character string.`) }) await test('Reject seed containing non-hex characters', async () => { - await assert.rejects(Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', TREZOR_TEST_VECTORS.SEED_0.replace(/./, 'g')), + await assert.rejects(Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.SEED_0.replace(/./, 'g')), 'Seed contains invalid hexadecimal characters.') - await assert.rejects(Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BLAKE2b', TREZOR_TEST_VECTORS.ENTROPY_1.replace(/./, 'g')), + await assert.rejects(Wallet.load('BLAKE2b', NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.ENTROPY_1.replace(/./, 'g')), 'Seed contains invalid hexadecimal characters.') }) await test('Import BIP-44 wallet from storage using a wallet-generated ID', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD) + const wallet = await Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD) const restored = await Wallet.restore(wallet.id) assert.ok('mnemonic' in restored) @@ -242,7 +242,7 @@ await Promise.all([ }) await test('Import BLAKE2B wallet from storage using a wallet-generated ID', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BLAKE2b', TREZOR_TEST_VECTORS.ENTROPY_0) + const wallet = await Wallet.load('BLAKE2b', NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.ENTROPY_0) const restored = await Wallet.restore(wallet.id) assert.ok('mnemonic' in restored) @@ -263,7 +263,7 @@ await Promise.all([ }) await test('export wallet IDs from storage and reimport them', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD) + const wallet = await Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD) const backups = await Wallet.export() assert.ok(backups.some(record => record.id === wallet.id)) diff --git a/test/test.lock-unlock.mjs b/test/test.lock-unlock.mjs index e15b0f6..727cf02 100644 --- a/test/test.lock-unlock.mjs +++ b/test/test.lock-unlock.mjs @@ -24,7 +24,7 @@ await Promise.all([ suite('Lock and unlock wallets', async () => { await test('locking and unlocking a Bip44Wallet with a password', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD) + const wallet = await Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD) assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) @@ -41,7 +41,7 @@ await Promise.all([ }) await test('fail to unlock a Bip44Wallet with different passwords', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD) + const wallet = await Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const lockResult = await wallet.lock() @@ -58,7 +58,7 @@ await Promise.all([ }) await test('fail to unlock a Bip44Wallet with invalid input', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD) + const wallet = await Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) //@ts-expect-error @@ -70,7 +70,7 @@ await Promise.all([ }) await test('locking and unlocking a Blake2bWallet with a password', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BLAKE2b', TREZOR_TEST_VECTORS.ENTROPY_0) + const wallet = await Wallet.load('BLAKE2b', NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.ENTROPY_0) assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) @@ -87,7 +87,7 @@ await Promise.all([ }) await test('fail to unlock a Blake2bWallet with different passwords', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BLAKE2b', TREZOR_TEST_VECTORS.ENTROPY_1) + const wallet = await Wallet.load('BLAKE2b', NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.ENTROPY_1) await assert.rejects(wallet.unlock(TREZOR_TEST_VECTORS.PASSWORD), { message: 'Failed to unlock wallet' }) assert.ok('mnemonic' in wallet) @@ -101,7 +101,7 @@ await Promise.all([ }) await test('fail to unlock a Blake2bWallet with no input', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BLAKE2b', TREZOR_TEST_VECTORS.ENTROPY_1) + const wallet = await Wallet.load('BLAKE2b', NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.ENTROPY_1) //@ts-expect-error await assert.rejects(wallet.unlock(), { message: 'Failed to unlock wallet' }) diff --git a/test/test.tools.mjs b/test/test.tools.mjs index fa8ae18..b9b1f5c 100644 --- a/test/test.tools.mjs +++ b/test/test.tools.mjs @@ -137,7 +137,7 @@ await Promise.all([ }) await test('should verify a block using the public key', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', NANO_TEST_VECTORS.BIP39_SEED) + const wallet = await Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const account = await wallet.account() if (account.index == null) { @@ -152,7 +152,7 @@ await Promise.all([ }) await test('should reject a block using the wrong public key', async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', NANO_TEST_VECTORS.BIP39_SEED) + const wallet = await Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const account = await wallet.account() if (account.index == null) { @@ -178,7 +178,7 @@ await Promise.all([ }) await test('sweeper fails gracefully for ineligible accounts', { skip: true }, async () => { - const wallet = await Wallet.load(NANO_TEST_VECTORS.PASSWORD, 'BIP-44', NANO_TEST_VECTORS.BIP39_SEED) + const wallet = await Wallet.load('BIP-44', NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const results = await Tools.sweep(rpc, wallet, NANO_TEST_VECTORS.ADDRESS_1) -- 2.47.3